In the midst of testing Carpe Diem's AI I found that something was eating up a lot more memory than I had expected while the AIs went head to head on some of the larger maps. In light of this I decided to spend much of my time with Carpe Diem since the last update profiling memory usage and making a few tweaks toward memory optimization.
After tweaking the Java virtual machine's memory arguments and analyzing memory consumption in a variety of situations I narrowed the culprit down to the faction boundary generator, the thread that determines and creates bezier curves to highlight the edges of each faction's territory.
Luckily this ended up being a pretty easy and straightforward fix. The generator makes use of a number of large list variables for a variety of reasons. To limit consumption and help the garbage collector reclaim more memory in a timely manner I made sure to clear each list as soon as it is no longer needed. While this certainly helped free things up a bit what really knocked the ball out of the park was re-working some of the code so I could switch the ArrayLists to LinkedLists.
ArrayLists can be quite useful, in particular if random access of the elements contained within the list is necessary. The problem here, when it comes to memory consumption, is that when the list needs to resize the new list size can be around 1.5 times the size required, this helps ensure the list doesn't need to create a new backing array with each addition, the trade off, of course, is additional memory consumption when the entirety of the new size is not required. Moreover during the resizing operation the backing array needs to be copied to a new array so, for a brief period, both arrays exist in memory.
The random access of elements benefit offered by ArrayLists was not at all necessary in this case. I re-worked my code so that I would only ever iterate over the lists in sequential order, something that LinkedLists work well with. The main benefit here is that LinkedList does not make use of a backing array, this makes random access quite time consuming, but saves on memory because the elements do not need to be copied to a new array when the list is resized nor does the list create an array of length greater than the number of elements it is required to hold.
There is one instance, when the generator is wrapping up, in which random access of elements in a list becomes necessary. That particular list is copied over to an ArrayList, however, all of the other much larger lists have already been cleared so memory consumption at this point is not of big concern, plus the size of the new ArrayList is known in advance ergo resizing doesn't become an issue. Ultimately I believe these enhancements not only reduced memory consumption, but also provided an always useful speed boost as items are frequently added to these lists throughout the generator which LinkedLists are much quicker at than ArrayLists, at least in the event where the array would need to be resized.
All of this profiling and tweaking still afforded me the opportunity to observe the AI in action. While there's certainly a few nuts and bolts to tighten ultimately I'm quite pleased with how the AI is handling itself even when fighting on multiple fronts.