The answer is probably no, but let’s explore. If you’re a Java developer, you’ve likely seen the following thrown in your face, it’s almost never expected and almost always being thrown because of an erroneous piece of code, so what should we do?
I find myself occasionally getting into heated debates with fellow developers about to do when we encounter an OoM issue, especially when developing for projects with tight deadlines. Should we add more memory? Should we log a ticket and come back to it later? Should we dive in right now and fix it? The opinion on what to do varies substantially depending on who you ask.
What makes up Java memory?
If you peruse through
the internetStackOverflow, you’ll find many comments simply telling you to increase the maximum memory allocation pool for the JVM (-Xmx), usually supplemented by telling you to modify the initial memory pool size (-Xms) too, but is this right? Let’s review some of the locations where your Java process is actually using memory.
- The heap is memory allocation that I find developers are most familiar with, it is where your Objects are stored. This is what’s affected by setting values for (-Xmx) and (-Xms).
- The metaspace as the name implies stores metadata throughout the processes runtime, this includes things metadata from objects you create for example.
- The code cache is where native code generated by the JIT is stored, this increases performance as this code can be reused and will not need to be recompiled. If you’re interested in knowing more about JIT you should check out this video.
- The importance of Buffer Pools and their use can often be missed on newer Java developers, in fact many of those that I have discussions with are unaware that it exists, or how to make use of it. Allocating memory to the buffers outside of the heap can improve performance, these pools can also be used to share code between native and Java code.
- In addition to the heaps and stacks managed by the JVM, the operating system itself manages additional heaps and stacks are that independent of those ran inside of the JVM.
- Every thread has it’s own call stack which stores object references along with primitive local variables. The list of method invocations (the call stack) is also stored here.
Knowing that there’s more to your Java process than just heap usage can be the first step in understanding whether or not increasing the memory allocation for the heap will help you.
Take a dump (or two) before increasing memory!
It is of course possible that your application really needs more memory, you’re trying squeeze a small shirt onto a large frame, increasing the amount of memory allocated to your application is of course the right thing to do here, but what if that’s not the case?
Providing more memory to your application isn’t free, it comes with overhead of increasing Garbage Collection Pauses which will impact your throughput, so before we do it let’s make sure that it is the right thing to do.
Getting your hands on a heap dump after an OoM can be fundamental in debugging and solving potential issues, the more dumps the better. Please be careful when acquiring these, dumps contain a lot of noise but depending on your applications purpose could also contain sensitive user information which should remain secure. If your organization has a SecurityOps there may be a few hoops to jump through before getting permission to grab one.
Analyzing and processing a heap dump can take a little bit of time, so be sure to take an afternoon when you sit down to do it.
Improve JVM Memory usage.
After you’ve debugged your dump, plugged your memory leak and deployed the fix to production there’s still a couple of improvements that could likely be made by tweaking your JVM settings, but before doing that monitor your fix! I’ve seen many deployments go out that developers assume have improved their service, you should verify it.
- Decrease your maximum heap (-Xmx)! It is likely already too large for your process and that can negatively impact performance.
- Select a Garbage Collector that’s right for your service, if you’re still in the Java 8 world you might be interested to know the default GC has changed in Java 9, check it out.
Don’t fall into the trap of simply increasing memory, it is typically a band aid solution that is masking more serious problems.