As both a programmer (whose strongest language is Java), and a fan of strategy video games, sometimes I like to put the two together. My longest-lasting project in this area has been a third-party editor for Civilization III, which started out back when I was in college primarily a challenge to see if I could write something of that complexity and of a comparable value to what some other members of the community had written, and evolved to offer some unique and easier-to-use features than other options (both official and community) provided for that game.
Along the way, I experimented with and learned various programming techniques. Early on (and when I had the most time for the project), I was still an amateur, learning a good amount in school, but the project was a testing ground for many of the things I was learning. Inevitably, there are areas of the code that I would write differently today, but I also learned useful lessons in modularization, parallelization, and several other techniques along the way.
On the parallelization note, I've always had some interest in performance; at various points I've added code to this project to make some of the lengthier and/or most common tasks perform better, or take advantage of multi-core processors. This has typically yielded noticeable improvements, although I always take care to measure that there is an improvement, and have occasionally seen changes that I thought would help performance do the opposite. But by and large, thinking about CPU performance and using multiple cores has worked out well, and it's the sort of mental challenge that I enjoy in programming. And, who doesn't like making things run faster.
I hadn't focused as much on memory performance. In part, this was because it wasn't really necessary. In typical usage, if the program is displaying a visual map, it uses 256 MB - 384 MB of memory - a low amount on today's machines - whereas it uses 32 - 64 MB without a map, depending on the complexity of the scenario. There are a couple edge cases where I could likely improve worst-cases RAM usage, but for the most part I'd focused on CPU performance, since that's where I could see more impact.
Recently, however, I got an older version of the project working on a Windows 98 laptop with 104 MB of total system RAM. And I was surprised, given the 32 MB baseline for a moderate-complexity scenario that I'd seen before, to see my program was using less than 8 MB of RAM. While this was certainly a pleasant surprise - even with just a shade over 100 MB of system RAM, my program was in no danger of running out of memory - it got me wondering what differences in Java and Windows were most responsible for the four-fold difference when running the same source code with the same data - only the versions of Java and the versions of Windows differed from what I typically used.
So tonight I ran the program, with the same input file, on 5 variants of Java on Windows 8.1 (Java 5 through Java 8, including both 32 and 64 bit on Java 7), and compared that to what I saw on Windows 98 SE with Java 5. The Windows 8.1 numbers are averaged across three trials; the Windows 98 SE is across two trials. The chart is below.
Perhaps unsurprisingly, the newer the Java or Windows version, for the most part, the higher the RAM usage. Java 8 clocked in at 32-33 MB; Java 7 was at 29-30 MB, and interestingly the x86 version only saved 1% of RAM versus the x64 version. It appears that fixed-width values used far more memory than pointers that varied in length in my program.
Java 6 and earlier, however, use far less memory. At 11-12 MB, Java 6 is quite slim. Java 7 was a significant revision, however I did not expect to see Java 6 using less than 40% as much memory. By the time Java 7 came out in 2011, the increase was negligible for this program, but it would be interesting to compare more programs and see what the general increase is for desktop (or server) Java programs.
Java actually used a bit more memory than Java 6 on Windows 8.1, at 13-14 MB. Meanwhile, this was almost 80% higher than the lightweight 7.4 MB that the same version of Java used on Windows 98. Windows 98 does not support any version of Java newer than Java 5, so I can't do an apples-to-apples comparison for newer versions, but either the Java implementation or the underlaying operating system structures do have a noticeable percentage-wise impact. Still, the Windows 98 versus Windows 8.1 difference is less percentage-wise than the Java 6 versus Java 7 difference, and considerably less in overall RAM usage.
Ultimately this is almost certainly an academic exercise for this scenario, as even if it took 300 MB, I doubt anyone would notice in today's age of cheap RAM. However, I do find the differences interesting. I've been reading about the design and construction of Windows NT lately, and the increased system requirements, including memory usage, of NT versus DOS-based Windows, and the difference seen here in 98 versus 8.1 is likely in part due to that. The differences in Java 6 versus Java 7 are the more surprising ones to me, as unlike the overhead required in NT to ensure stability, prevent an application crash from bringing down the operating system, and so forth, nothing springs to mind explaining why Java 7 would double memory usage over Java 6. I do suspect that the particular application type - a desktop Swing application - may cause the difference to be larger than for other common Java use cases, but would be curious if there's a low-level change that would explain the leap.
I'll likely test this a bit more on Windows XP (both 32 and 64 bit), to get another data point along the Windows timeline, and update this post with more information on the program and test data, as well as the XP results, test methodology, and specific Java updates used. My guess is that XP will hew more towards 8.1 in its memory usage, but I could be surprised.Return to Blog Index