Using JRE -Xms300m cut my execution time in half?
Greetings all, I was wondering if someone could provide some sort of explaination or confirm my guesses.
I had a program with a for loop which executed several hundred of thousand times, each time adding numbers to a selection of ArrayLists. Confused as to why it was taking so long to execute I investigated further.
It turns out that if I had
(for int i =0; i < 50000; i++)
It would take almost double the time It would take if I had
(for int i=0; i < 40000; i++)
Increasing i
beyond 50000 even up to 100000 didn't actually take much extra time. There was just this large jump somewhere between 40000 and 50000
Putting my thinking hat on, I gathered that perhaps there was an issue whereby something somewhere was running out of memory? and that more memory had to be make availble, though I'm not sure why it would take so long.
Anyway, I found that by adding the JRE parameter -Xms300m it solved this issue. Am I right in thinking this starts the program with a heap size of 300mb, thus negating the need for extra heap space to be allocated开发者_开发百科 later.
What else I don't understand is that I created memory for the arrayLists which is more than enough. I thought the problem would happen here when I am allocating memory of the heap, not when I use the .add() method.
new ArrayList<Integer>(5000);
Yess, the -Xms options specifies the starting heap space. If this sped up your problem it is a good bet that you were running into memory allocation/garbage collections issues. Note the ArrayList API:
"Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.
An application can increase the capacity of an ArrayList instance before adding a large number of elements using the ensureCapacity operation. This may reduce the amount of incremental reallocation. "
So, how the ArrayList adds memory is an implementation detail of your JVM, but has constant amortized cost.
I bet you are creating objects in your array, that between 40k and 50k iterations your program was trying to GC, failing, then adding more heap. Specifying a higher minimum would delay GC and more heap creations...
By specifying an initial size for the ArrayList
you're reserving space for references to the elements, not the elements themselves. Each time you call add()
, a new Integer
object is created and added to the underlying array, and it's most likely these objects that are eating up your heap.
I'm a little surprised that just adding -Xms300m helped - this sets the minimum heap size, but without also setting the maximum (with -Xmx) you'd normally see an error:
$ java -Xms300m blah
Error occurred during initialization of VM
Incompatible minimum and maximum heap sizes specified
When a Java app creates lots of objects and approaches the heap limit, the JVM starts to perform garbage collection (GC), which essentially involves two steps:
- hunting for objects that are no longer in use and can be freed up
- consolidating objects that have been around for a while into a different part of the heap (the "old" or "tenured generation") - this means that future GC runs can ignore these objects, improving performance
There's lots of in-depth documentation on Java's GC mechanisms (including the daddy of them all) but for starters, to see what's happening in your program try adding the -verbose:gc
flag. This outputs a line each time the garbage collector kicks in, telling you how long it took to run and how much memory was freed up, which might give you clues as to what's happening in the heap when you increase the number of iterations.
@hvgotcodes's answer says this:
I bet you are creating objects in your array, that between 40k and 50k iterations your program was trying to GC, failing, then adding more heap. Specifying a higher minimum would delay GC and more heap creations...
This is close to the mark, but probably not exactly right.
With Java 1.6 Hotspot JVMs, the heap will get expanded in two situations:
After garbage collection, there is still not enough free space to allocate the object that triggered the GC.
After garbage collection, the ratio between the amount of free space and the amount of used space is less than a given value. The default ratio is 40%, but this can be adjusted.
In the OP's example, it is difficult to know which is occurring. However, it is not hard to imagine applications where the first rule never applied. For example, some application might fill the current heap to 99%, and then keep allocation lots of tiny objects. Without the second rule, the heap wouldn't be expanded and overall performance would suffer.
精彩评论