Monday, 25 September 2017

Lots of Old Gen GC in my JVM

Frequent Old Gen GC issues with JVM

Garbage collection in JVM is more about not moving short lived  objects to Old Gen and also not keeping long lived objects too long in Young Gen. So, as you can see there is always a compromise between the two.

Some known issues causing  high GC ::

1) Check you logger version. Some logger version do create a lot of logging objects even on INFO mode. Some are know to still create object of debug and trace level despite of a higher logging mode.
Choose an appropriate logger version which is not known to cause GC issues.

2) Choose the correct serialisation and deserialisation technique in you application. As an example, bytes should be directly deserialised to the required object and not to String and then the required objects.

3) Be careful in using string objects. Do not create too many strings and objects on every request. Keep strings/objects cached whenever possible. And use Singletons whenever possible.

How to tune GC ::

1) Try the new G1 garbage collector. This is the best default GC.

2) If there are too many old gen GC's, and if the old gen GC time is too long, then increasing the Young Gen size is required. Keep on increasing the Young Gen size to see the reduction in the frequency of Old Gen GC and their time.

3) If step1 does not work, then consider increasing the overall heap space(-Xmx) for your JVM. Be careful to leave enough free memory for the OS. Otherwise memory swaps can start to happen.
Increasing the max heap size and accomodating this increase in Young Gen is a known solution to decrease the number of old gens.


Saturday, 20 May 2017

Benchmark your code

Objective: Let us discuss a few common techiques of benchmarking your code/service and similar concepts. These concepts are a general understanding which I have developed from time to time and would only go into the very simple and basic benchmarking techniques.


Q1: There are many profilers for profiling a JVM code. But is there a reliable way to quickly get the cpu time of my code ?


A: Yes there is a quick way. And "java.lang.management.ManagementFactory" provides the solution. ThreadMXBean's getCurrentThreadCpuTime will give you a much accurate cpu time. Do refer to the below test code which demonstrates that the sleep and wait of several milli seconds is correctly discarded from the total cpu time by getCurrentThreadCpuTimecodeLink::MeasureCpuTime. This is a nice example profiling a sum method to output the sum of all elements in a list.
I will be a very good practice to perform such instrumentation for all important code blocks in unit testing itself. A simple profile function as mentioned in the above code will do the same.

Q2: Hey! In the above profiling, I found that the first run is always taking way more than the the subsequent runs. Why is it so ?

A: Yes. It is because in the first run the method ("sum") is invoked for the first time. There is an extra CPU cost associated with loading that particular method in code cache of heap. This extra cost is only for the first invocation. Also, for subsequent runs the JVM hotspot would have further optimised the byte code especially by removing the redundant operations corresponding to your method.

Q3: Is there any simple way to benchmark the heap foot print of my code ?

A: Yes there is a way. But, actual CPU time profiling is much easier in theory. Again ManagementFactory provides you a function (getGarbageCollectorMXBeans) to get all garbage collection information. The code link link::MeasureHeapHotspot shows the heap usage pattern for fold list to string operation. Notice how the heap usage is always high in the unoptimised approach. And the optimised approach is not only readable but also has no heap footprint in the subsequent runs.
                Despite of this explanation, there are many easily available instrumentation and profiling libraries. It is better to use these libraries or build something on these lines in the long run. Also, ideally those instrumentation metrics should be pushed via mbeans and not by print statements :)


more to follow ....