For Java programmers the garbage collector is a pretty nice thing. Peter wrote a bit about Java and the garbage collector on this blog a while ago. In short, the garbage collector will make sure everything that isn’t needed anymore will just quietly disappear. Most of the time you’ll never know it is there. And after a while you sort of forget its existence, and when you do encounter it, it might be a painful experience.
I had one of those encounters when I was nearly finished with our new render-core and rolled it out to all developer machines. One of the laptops (with only an AMD APU) suddenly started running out of video memory very fast. That was strange, the memory footprint of Caromble! isn’t big and hasn’t changed with the new core. So what was happening?
After a bit of debugging it proved related to the rendering of text. Each time some in-game text would change, we would allocate a new vertex array on the GPU with exactly enough space for each letter. For each text (and the game has about 5 of these at any one time) only one buffer was used. Hardly exciting and not something you would expect to cause problems with video memory. Nevertheless, these buffers were causing the problem.
I looked at how the video buffers were disposed. All references to a buffer on video RAM (with a backing system RAM copy) were being managed by a ReferenceQueue. It is a solution from theArdor project that I think is both elegant and powerful. As long as the data can still be used it will remain on the video card. When it can’t be reached anymore, because all references to it are destroyed, it will be scheduled for garbage collection. If we poll the RefenceQueue regularly we’ll get a reference to all buffers scheduled for destruction and can tell the GPU that it can free the corresponding vertex array. So it isn’t necessary to keep track of who is referencing the data, but we still get to execute custom code for cleaning up the memory from the GPU. Perfect right?
Except it isn’t. As soon as the garbage collector detects that a video buffer can’t be used anymore it will return a reference to the object holding that buffer for us. But there lies the problem. We won’t get that reference until the garbage collector determines that it can safely be removed. The only guarantee the JRE makes on this matter is that this will happen before Java runs out of heap-space. So as long as there is sufficient system RAM available the garbage collector might decide that it will be better for performance to just let the garbage lie around for a bit longer. Since it doesn’t know about the video memory we are using, it can’t know that leaving this bit of garbage can be dangerous.
So that was the lesson I learned from all this? The job of the garbage collector is to keep the program inside its heap-space and to try to be as silent about it as possible. If there is enough space there is no need to slow the program down by collecting. So it won’t.
Once I figured out what the problem was, the solution was quite simple (but not very elegant). I solved the problem by explicitly removing buffers that I know are no longer required from the GPU. Buffers that are potentially shared by different meshes are kept on the GPU until the backing CPU data gets garbage collected. But since these buffers don’t often changed that is not a problem.
It was quite an interesting insight in the workings of the garbage collector, and I think it also proves that you shouldn’t try to to be too smart around resources allocated out of sight of the garbage collector.