What's likely occurring is you're creating a lot of new classes during your looping. One of the best ways to handle this is to reuse objects which can be reused. However, I recommend running your code through the Profiler first so you can get a break down of which classes are building up the most data.

In your renderer, in asFlippedFloatBuffer, cache a ByteBuffer and only use ONE (make it static and what not).

You call that ALOT and each time it creates a DirectByteBuffer... which allocates a chunk of memory outside the JVM... and when the JVM sees that it's not in use it cleans it and removes it. This could take time and they could add up.

You're not alone in this problem. I'm fairly certain you're running up against the exact problem I encountered, because I built my own objects for handling matrices and vectors.

Also like you, I figured arrays would be the most natural way of handling this. Now you can still do it with arrays if you really want, but here's what I found:Arrays are objects, so every time you create one of those, you end up with the potential for garbage. This problem is compounded when you do things like set the Matrix by using a new float[] or have it return a new float[].

You may be wondering how you handle passing around matrices and stuff without referencing the original matrix. I mean if you pass a matrix to a method and have it mangle the bastard, it's going to destroy the original copy you wanted to keep, since Java behaves like objects are pass by reference (pedantic note: My understanding is Java is considered pass by value).

The answer comes from a seemingly unlikely place. Since Java handles objects like references and primitives as values, it's actually easier to to set your Matrices and Vectors to contain primitives. For me it felt like this goes against what you're taught in programming 101. But like everything with code, it's always subjective to the what you need it for.

And of course the same thing as with the vectors.Now you can do all the multiplication and what not by passing matrices into other matrices and then do your operation using the primitives.Here's an example of setting a matrix from another matrix

This way you avoid linking two matrices together by reference. The above method will create an entirely new set of values from the matrix which was passed in.

This actually turns out to be quite fast. I was a little concerned that using primitives like this would eventually lead slowing things down. So I did some testing and it turns out Java is very fast at setting primitive values. And since you'll only handle up to 16 values, you're not losing out by skipping arrays.It also makes accessing individual cells kind of nice.

Conclusion:I started down the rabbit hole of correcting my math, because I encountered the stuttering effect due to garbage collection. Before I fixed everything, I noticed my used memory was accumulating faster than an ex-wife could amass debt with her ex-husbands credit card. It was knocking over hundreds in seconds.Now that I've cleaned the math up and reused objects like matrices, it takes a couple seconds before I even see it kick over 1 MB. I imagine you'll encounter the same.

And like the others have said before me, reusing your Buffers will also go a long way.

In the end, I think most of us go through this at one point or another. So don't let it get you down.

In your renderer, in asFlippedFloatBuffer, cache a ByteBuffer and only use ONE (make it static and what not).

You call that ALOT and each time it creates a DirectByteBuffer... which allocates a chunk of memory outside the JVM... and when the JVM sees that it's not in use it cleans it and removes it. This could take time and they could add up.

Could you give me an example on that as my buffers are varying in sizes? I was thinking of using a HashMap to store buffers in (if a key doesn't have a value, generate it - if a key has a value, clear and re-insert values)

I don't do the fast graphics like you do; I tend more towards the turn-based, and my problems tend to be more about checking who's near whom and what's the shortest path to somewhere. (And I write business software too.) And the GC problem usually shows ups as an out-of-memory exception (despite lots of free memory) rather than a slowdown. But your problems and mine are likely related, so:

The key word is Fragmentation. It's been a long time since I've had a program hesitate while the GC runs. The GC that comes from Oracle with the JVM does a superb and invisible job. I don't think even you should notice that it's running, no matter how many new objects you create. But if you create large objects (arrays), then the GC has to look harder to find the contiguous space for them. When you've created--and freed--a bunch, your free memory is mostly large-object-sized chunks with, perhaps, pieces taken out for small new allocations. Now if you try to allocate a new large object, there's no space quite big enough or contiguous enough. Your memory is fragmented.

The GC will work frantically to move allocated memory around to turn smaller free chunks into bigger chunks. If your program used memory at a leisurely pace (if your game ran 10 times slower), you'd never notice a problem. As it is, you are most likely seeing the resultant slowdown.

If you ignored it and let it get worse, eventually the GC would prefer throwing an exception to embarrassing itself with such abysmal performance. (That's what usually happens to me.)

My solution is to avoid arrays and things that use them like HashMaps and ArrayLists and use TreeMaps and LinkedLists instead. This would, most often, not work for you; arrays really are much faster than, say LinkedLists, even for sequential reading. So my answer to your problem is, like most of the other ones: keep your arrays as small as you can and reuse them. The new arrays are the problem. (I'm putting this answer here to explain why this is happening; the solution's been given several times over.)

(Also, making all your arrays the same size might help. The classic way to get this problem is to add millions of items to an ArrayList. Each time the ArrayList resizes, it needs a bigger array. Once the original memory is used up, free memory is fragmented into many large blocks, but none of them big enough for the next allocation.)

If the bulk of the memory usage is from DirectByteBuffers, then heap fragmentation isn't so much the issue, or rather not the java heap anyway. Every direct buffer you allocate has to be malloc()'d individually from the native heap, and creates a PhantomReference and a Cleaner object that will free() it when it goes out of scope. Each one, individually. In short, while using direct buffers is ridiculously fast, allocating them is a very heavyweight operation. You're meant to reuse them as much as possible.

ArrayList won't reallocate every time you resize it, and will pre-extend itself by quite a bit. However, it actually tends to err on the side of pre-extending by too much which puts additional pressure on the GC for space you don't need or use. If you have a good estimate on the number of items you're putting into a collection, it's always a good idea to instantiate it with the size hint and not the default of 10.

ArrayList won't reallocate every time you resize it, and will pre-extend itself by quite a bit. However, it actually tends to err on the side of pre-extending by too much which puts additional pressure on the GC for space you don't need or use. If you have a good estimate on the number of items you're putting into a collection, it's always a good idea to instantiate it with the size hint and not the default of 10.

and use trimToSize to free up memory, if you're not too sure what data size you're dealing with upfront, and are running low on free memory. ArrayList at most allocates 50% too much memory (doubling the backing array every time) which is normally not enough to worry about. (C devs, will scream at me, I'll look the other way )

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

The problem I was referring to--whether or not the OP's problem is another matter--is the too-rapid allocation and deallocation of large blocks of memory; it's got little to do with using a lot of memory or wasting memory. The most typical symptom is getting an out-of-memory exception with 90% of memory free. The simplest way to do it is with something like:

If the optimizers don't interfere, this will, of course, throw an out-of-memory exception. What is unusual is that when it does a lot of memory will still be free. (There must be a dozen questions about this on StackOverflow.) The reason for the OOME is not that there's no memory left, but that the GC is taking too long to merge free blocks and gives up rather than slow down too much.

Most people see the OOME and wonder why. I think game writers are more apt to see the slowdown and wonder why.

Reducing the size of allocated memory helps a lot. Reusing big arrays helps a lot. Doing lots of other work between allocations (giving the GC thread time to do it's job) helps, too.

I was surprised by your findings and was absolutely sure you were absolutely wrong

The GC strategies (since about 15 years) don't even merge free memory blocks. Garbage collectors don't collect garbage, so to speak, they copy live objects into a new memory block, completely ignoring free space. When everything is copied, the whole original space is declared free for future use. That means that fragmentation is simply not an issue with Java objects and arrays - only for direct buffers, as they are backed by malloc/free algorithms.

Equally, the GC doesn't throw its hands into the air and call it quits when it has to work really hard. There is one case, however, and that is that when the GC concludes that it is collecting garbage over 99% of the time, for an extended period (say, minutes), it will conclude it's infeasible to continue running. This doesn't happen in practice, unless you're paging massive amounts of memory to disk and back, at which point your game/service is dead in the waters anyway.

The GC will give up if it's unable to free space for allocation while meeting its pause time and throughput goals. As usual, the Java GC Tuning doc is a handy reference for the various knobs that control GC throughput among other things. It's of little help when it comes to direct buffers though, since those are allocated much more primitively and slowly on a different heap.

I haven't tried this recently, and when I last had the problem I was trying to do something else completely. Still, I've heard of enough other people with the problem that I doubt it's gone away. I am beginning to wonder if my ArrayList Add test does much to demonstrate it.

I would suggest adding a new object rather than null to the array. What you've got will, if I still understand ArrayList, fill memory consecutively with arrays, each 50% larger than the previous. All but the last two will be freed, and the next to last one will usually be freed. When allocations hits the end of available memory, they can easily go back to the start and reuse what's there. Adding actual objects will put those objects between each array. Now the GC has some real work to do. I can't guarantee a proper out-of-memory exception with 90% of memory still free, but you should see something of interest.

It seems to me that fragmentation still must be an issue. To allocate space for a large array, you need a bunch of contiguous memory blocks with nothing in them. With allocated bits of memory scatter through them, that means a lot of copying.

LinkedLists are slow memory hogs, but they only ask the GC to do a tiny amount of work at a time.

When allocations hits the end of available memory, they can easily go back to the start and reuse what's there. Adding actual objects will put those objects between each array. Now the GC has some real work to do.

You really have to let the idea of costly merges of free-lists go. Inserting objects between arrays really won't matter, due to the nature of the GC algorithms, as explained earlier, they copy alive objects and arrays to a new memory block, meanig that there simply are no free lists to merge.

LinkedLists are slow memory hogs, but they only ask the GC to do a tiny amount of work at a time.

They actually require the GC to do a lot of work, because it has to trace extremely deep object graphs of reachable objects, which is what will slow everything down tremendously for every single garbage collection, even when all objects reside in Eden.

@Riven: You're right. I have seen big-array problems;whether that was an old GC or whether I need a fancier test to reproduce it I don't know.

I ran the thing on an antique machine with a 250MB heap and a single core. I added an actual object (an 8 character string, created with "new"--just using quotes gave me the same results as using null). Using the object slows down the ArrayList so it's only about half again as fast as the linked list and can only hold twice as much before the out-of-memory error. (Without it, my results were like yours, scaled down by a factor of 8. I printed a message and time every 131072 (1024X128) lines.

The results between the two lists were amazingly similar. The ArrayList needed 20 milliseconds between rows and the LinkedList 30. (Its a slow machine, and the smallest measurable time interval is 10 milliseconds.) About every ten to 12 messages, the time needed spiked to 500 to 2000 ms, getting larger as the program ran. I looked to me like the program was doing repeated garbage collections. These would be needed for the ArrayList, but in the case of the LinkedList, until the last one there was plenty of free memory and, at no point, was any memory freed!

As you noted, arrays take less time in gc, even when there is array space to be collected and no LinkedList space to be collected.

I'm not sure what to make of all this. It seems to slow down code that doesn't free large arrays without helping those that do. But it is interesting.

You really have to drop the concept of collecting garbage and freeing memory. That's not what a garbage collector does

Think of it as a room with gadgets, toys, empty pizza boxes and dead rats. Mom comes in, takes handful of toys and gadgets, puts them in brother's room, then locks your room. Whatever filth was there is out of reach and can be completely ignored. (if we go further, the analogy breaks apart... let's say uncle bob effortlessly atomizes whatever is left in your room, and pumps it in your 3d printer reservoir, for you to print new pizza boxes, which attract dead rats, aight!)

Hi, appreciate more people! Σ ♥ = ¾Learn how to award medals... and work your way up the social rankings!

Think of it as a room with gadgets, toys, empty pizza boxes and dead rats. Mom comes in, takes handful of toys and gadgets, puts them in brother's room, then locks your room. Whatever filth was there is out of reach and can be completely ignored. (if we go further, the analogy breaks apart... let's say uncle bob effortlessly atomizes whatever is left in your room, and pumps it in your 3d printer reservoir, for you to print new pizza boxes, which attract dead rats, aight!)

You really have to drop the concept of collecting garbage and freeing memory. That's not what a garbage collector does

Think of it as a room with gadgets, toys, empty pizza boxes and dead rats. Mom comes in, takes handful of toys and gadgets, puts them in brother's room, then locks your room. Whatever filth was there is out of reach and can be completely ignored. (if we go further, the analogy breaks apart... let's say uncle bob effortlessly atomizes whatever is left in your room, and pumps it in your 3d printer reservoir, for you to print new pizza boxes, which attract dead rats, aight!)

Very nice explanation. Thanks!

So if the gc does not free garbage collected objects, what happens to the collected garbage? Does it just stay there infinitely until program closes?

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org