Java Garbage Collection
In this blog post, I'm briefly introducing important concepts in Java Garbage Collection (GC) and how to do GC Logging. There are many resources available online for Java GC and I'm linking some of those in this post.
Why Garbage Collection is important?
When we develop and run Java applications, we know that Java automatically allocates memory for our applications. Java also automatically deallocates memory when certain objects are no longer used. As Java Developers, we don't have to worry about memory allocations/deallocations as Java takes care of the task to manage memory for us.
This memory management is a part of "Automatic Garbage Collection", which is an important feature in Java. It is important to know how Garbage Collection manages memory in our programs.
See Java Garbage Collection Basics, which is a great "Oracle by Example (OBE)" tutorial to understand the basics in Java GC.
See also Java Garbage Collection Distilled, What is Garbage Collection?, Java Garbage Collection Introduction, JVM performance optimization, Part 3: Garbage collection and the whitepaper on Memory Management in the Java HotSpot™ Virtual Machine
Java GC is also an important component when tuning performance of the JVM.
Marking and Sweeping Away Garbage
GC works by first marking all used objects in the heap and then deleting unused objects. This is called a mark-and-sweep algorithm.
GC also compacts the memory after deleting unreferenced objects to make new memory allocations much easier and faster.
JVM references GC roots, which refer the application objects in a tree structure. There are several kinds of GC Roots in Java.
- Local Variables
- Active Java Threads
- Static variables
- JNI references
When the application can reach these GC roots, the whole tree is reachable and GC can determine which objects are the live objects.
Java Heap Structure
Java Heap is divided in to generations based on the object lifetime. This allows the GC to perform faster as the GC can mark and compact objects in particular generation. Usually in Java applications, there are many short lived objects and there will be less objects remaining in the heap for a long time.
Following is the general structure of the Java Heap. (This is mostly dependent on the type of collector).
Java Memory |
There are three Heap parts.
- Young Generation
- Old Generation
- Permanent Generation
We can define the heap sizes with JVM arguments. See Java Non-Standard Options.
Following are some common arguments.
- -Xms - Initial heap size
- -Xmx - Maximum heap size
- -Xmn - Young Generation size
- -XX:PermSize - Initial Permanent Generation size
- -XX:MaxPermSize - Maximum Permanent Generation size
It's good to know that the java process may consume more memory than Xmx and there can be OutOfMemoryError (OOM) even when you think that your data structure should be able to fit within heap.
Young Generation
Young Generation usually has Eden and Survivor spaces.
Old Generation
This stores long surviving objects. When this fills up, a major GC (full GC) happens. A major GC takes a longer time as it has to check all live objects.
Permanent Generation
This has the metadata required by JVM. Classes and Methods are stored here. This space is included in a full GC.
In Java 8, the PermGen is removed.
"Stop the World"
When certain GC happens, all application threads are stopped until the GC operation completes. These kind of GC events are called as "Stop the World" events/pauses.
When GC tuning, one of the main targets is to reduce the time for "Stop the World" pause.
Java Garbage Collectors
Following are some garbage collectors available in Java 7 and there are different scenarios to use those. See Java Garbage Collectors in OBE tutorial, Types of Java Garbage Collectors, Garbage Collection in Java (1) - Heap Overview and Understanding Java Garbage Collection.
- The Serial GC
- The Parallel Scavenge (PS) Collector
- The Concurrent Mark Sweep (CMS) Collector
- The Garbage First (G1) Collector
See Java HotSpot VM Options for specific flags to enable above collectors.
My test runs revealed that the Parallel GC and the Parallel Old GC flags activate the Parallel Scavenge Collector (Java 1.7.0_80).
Following are some of my observations when using different collectors with Java 7 (I got the Young & Old Garbage Collectors from the GC configuration tab after opening a Java Flight Recording in Java Mission Control).
Following are some of my observations when using different collectors with Java 7 (I got the Young & Old Garbage Collectors from the GC configuration tab after opening a Java Flight Recording in Java Mission Control).
Name | JVM Flag | Young Collector | Old Collector |
---|---|---|---|
Serial GC | -XX:+UseSerialGC | DefNew | SerialOld |
Parallel | -XX:+UseParallelGC | ParallelScavenge | ParallelOld |
Parallel Old | -XX:+UseParallelOldGC | ParallelScavenge | ParallelOld |
Parallel New | -XX:+UseParNewGC | ParNew | SerialOld |
Concurrent Mark Sweep | -XX:+UseConcMarkSweepGC | ParNew | ConcurrentMarkSweep |
Garbage First | -XX:+UseG1GC | G1New | G1Old |
JVM GC Tuning Guides
See following:
- Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
- Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide (For Java 8).
- How to Tune Java Garbage Collection
System.gc()
A GC can be triggered by calling System.gc() from a Java program. However, a call to System.gc() does not guarantee that the system will a run a GC.
Using this method is not recommended and we should let the JVM to run GC whenever needed.
The finalize() method
An object's finalize() method is called during GC. We can override the finalize method to clean up any resources.
GC Logging
There are JVM flags to log details for each GC. See Useful JVM Flags – Part 8 (GC Logging)
See Understanding Garbage Collection Logs.
Following are some important ones. Last two flags log the Application times
Flag | Description |
---|---|
-XX:+PrintGC | Print messages at garbage collection |
-XX:+PrintGCDetails | Print more details at garbage collection |
-XX:+PrintGCTimeStamps | Print timestamps at garbage collection |
-XX:+PrintGCApplicationStoppedTime | Print the application GC stopped time |
-XX:+PrintGCApplicationConcurrentTime | Print the application GC concurrent time |
Note: "-verbose:gc" is same as "-XX:+PrintGC".
The "-Xloggc:" flag can be used to output all GC logging to a file instead of standard output (console).
Following flags can be used with "-Xloggc" for log rotation.
Note: Evan Jones has found that JVM statistics cause garbage collection pauses.
Following flags can be used with "-Xloggc" for log rotation.
Flag | Description |
---|---|
-XX:+UseGCLogFileRotation | Enable GC log rotation |
-XX:NumberOfGCLogFiles=n | Set the number of files to use when rotating logs, must be >= 1. Eg: -XX:NumberOfGClogFiles=100 |
-XX:GCLogFileSize=size | The size of the log file at which point the log will be rotated, must be >= 8K. Eg: -XX:GCLogFileSize=8K |
Note: Evan Jones has found that JVM statistics cause garbage collection pauses.
Viewing GC Logs
GCViewer |
Summary
This blog post briefly introduced Java Garbage Collection, Java Heap Structure, Different Type of Garbage Collectors and how to do GC logging. I strongly recommend to go through the links and read. Those resources have much more details and this blog post is just a summarized post on Java GC.
Some resources have outdated information and it's better to run some sample programs and try out.
I used sample Java2D demo as explained in OBE tutorial to test different garbage collectors. If you used my Java Installation Script, all Java demos will be installed inside $JAVA_HOME/demo.
Following are some example commands I used.
Following are some example commands I used.
#Default Collector $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -XX:PermSize=20m -XX:MaxPermSize=40m -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #Serial GC $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=40m -XX:+UseSerialGC -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #Parallel GC $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=40m -XX:+UseParallelGC -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #Parallel Old GC $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=40m -XX:+UseParallelOldGC -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #Concurrent Mark Sweep GC $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=40m -XX:+UseConcMarkSweepGC -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #Parallel New GC $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=40m -XX:+UseParNewGC -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #Garbage First (G1) collector $JAVA_HOME/bin/java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Xmx20m -Xms3m -XX:+UseG1GC -jar $JAVA_HOME/demo/jfc/Java2D/Java2Demo.jar #GC Logging #-XX:+PrintGCDetails -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime -Xloggc:gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=100 -XX:GCLogFileSize=1024K
Comments