Low GC in Java: Use primitives instead of wrappers

Overview
There are two good reason to use primitives instead of wrappers where possible.

  • Clarity. By using a primitive, you are making it clear that a null value is not appropriate.
  • Performance. Using primitives is often much faster.

Clarity is often more important than performance, and is the best reason to use them. However, this article discussed the performance implications of using wrappers.

I have had a lot of interest in this article How to avoid Garbage Collection, however this was lacking in much practical detail. This is the first article in a series on ways to reduce demands on the GC.

Performance of using wrappers
The following micro-benchmark behaves in a way many application do.

Loop using Wrappers and Wrapper Collection

Map<Integer, Integer> counters = new HashMap<Integer, Integer>();
int runs = 20 * 1000;
for (Integer i = 0; i < runs; i++) {
    Integer x = i % 12;
    Integer y = i / 12 % 12;
    Integer times = x * y;
    Integer count = counters.get(times);
    if (count == null)
        counters.put(times, 1);
    else
        counters.put(times, count + 1);
}

This creates objects for each task. While it is common practice to use int for loop counters its is also common practice to use Iterator You can play around with the types and parameters of this micro-benchmark, however you get a memory profile which will be familiar to many developers who have tried to tune their application. Using VisualVM the heap usage looks like this over a five minute period.

There was 20 minor GCs in about 6 minutes.
The average time of each loop is sub-microsecond which is pretty fast.

Took 4,099 ns per loop
Took 559 ns per loop
Took 115 ns per loop
Took 240 ns per loop
Took 255 ns per loop
In the first test, the JVM hasn’t warmed up.

Can using primitives really make much difference?

Performance of using primitives
The following benchmark behaves rather differently to most applications. Even though it is doing the same work as the previous benchmark, there is no objects created.

Loop using Primitives and array

int[] counters = new int[144];
int runs = 20 * 1000;
for (int i = 0; i < runs; i++) {
    int x = i % 12;
    int y = i / 12 % 12;
    int times = x * y;
    counters[times]++;
}

and the heap usage reflects this

There was no GCs over a period of 5 minutes. The test could have run longer and still not triggered a GC.
And the average time per loop is much lower as well

Took 198 ns per loop
Took 17 ns per loop
Took 16 ns per loop
Took 14 ns per loop
Took 15 ns per loop

In the first test, the JVM hasn’t warmed up.

Conclusion
Using primitives will perform better. (Unless there is excessive boxing and unboxing)

Even in applications where performance is not critical, it will improve clarity, both of the code and when you do attempt to profile your application, it will reduce the level of “noise” making it clearer as to what the problem is.

Notes
Even in the test where few objects were created, you can see some object allocation. This is mostly due to VisualVM’s polling. To reduce this I changed the polling interval from 3 seconds to 20 seconds.

The Eden size was increased to make the graphs clearer with -XX:NewSize=100m This value is not recommend (except perhaps for micro-benchmarks) but its is a parameter you may need to tune for your application.

Full code

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

2 Responses to "Low GC in Java: Use primitives instead of wrappers"

  1. Nved says:

    I think that if the first benchmark had an array instead of Map, it would be faster but surely not faster than the second

  2. I am perticularly interested in knowing which tool you used to generate these graph. Is it available for eclipse?

Leave a Reply


− one = 4



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books