Java is one of the heavily used languages in enterprise application development. One of the key features of the Java language is it's capability to clear out memory automatically. This gives application developer more freedom to think about his business logic rather than worrying about memory management of the application. This may be the utmost reason for the selection of java language for complex business application development.
Java uses a technology called Automatic Garbage Collection (GC) for clearing out any unused memory from your application. During this blog post, I will be discussing about Java memory model and how GC works within the Java virtual machine (JVM). In fact every java application runs on top of its own JVM.
Java Memory model
Each thread running in the Java virtual machine has its own thread stack. The thread stack contains information about what methods the thread has called to reach the current point of execution. I will refer to this as the "call stack". As the thread executes its code, the call stack changes. The thread stack also contains all local variables for each method being executed (all methods on the call stack). A thread can only access it's own thread stack. Local variables created by a thread are invisible to all other threads than the thread who created it. Even if two threads are executing the exact same code, the two threads will still create the local variables of that code in each their own thread stack. Thus, each thread has its own version of each local variable.
All local variables of primitive types ( boolean, byte, short, char, int, long, float, double) are fully stored on the thread stack and are thus not visible to other threads. One thread may pass a copy of a primitive variable to another thread, but it cannot share the primitive local variable itself. The heap contains all objects created in your Java application, regardless of what thread created the object. This includes the object versions of the primitive types (e.g. Byte, Integer, Long etc.). It does not matter if an object was created and assigned to a local variable, or created as a member variable of another object, the object is still stored on the heap.
A local variable may be of a primitive type, in which case it is totally kept on the thread stack.
A local variable may also be a reference to an object. In that case the reference (the local variable) is stored on the thread stack, but the object itself if stored on the heap.
An object may contain methods and these methods may contain local variables. These local variables are also stored on the thread stack, even if the object the method belongs to is stored on the heap.
An object's member variables are stored on the heap along with the object itself. That is true both when the member variable is of a primitive type, and if it is a reference to an object.
Static class variables are also stored on the heap along with the class definition.
Objects on the heap can be accessed by all threads that have a reference to the object. When a thread has access to an object, it can also get access to that object's member variables. If two threads call a method on the same object at the same time, they will both have access to the object's member variables, but each thread will have its own copy of the local variables.
Java Heap Memory
As discussed in the previous section, Java heap memory is responsible for storing all the objects created during the runtime of a program and all the member variables and the static variables with its class definitions. This is the area of memory which needs to be carefully controlled. Below diagram depicts the memory model of the JVM heap.
Young Generation
Most of the newly created objects are located in the Eden memory space.
When Eden space is filled with objects, Minor GC is performed and all the survivor objects are moved to one of the survivor spaces.
Minor GC also checks the survivor objects and move them to the other survivor space. So at a time, one of the survivor space is always empty.
Objects that are survived after many cycles of GC, are moved to the Old generation memory space. Usually it’s done by setting a threshold for the age of the young generation objects before they become eligible to promote to Old generation.
Old Generation
Old Generation memory contains the objects that are long lived and survived after many rounds of Minor GC. Usually garbage collection is performed in Old Generation memory when it’s full. Old Generation Garbage Collection is called Major GC and usually takes longer time.
Stop the World Event
All the Garbage Collections are “Stop the World” events because all application threads are stopped until the operation completes.
Since Young generation keeps short-lived objects, Minor GC is very fast and the application doesn’t get affected by this.
However Major GC takes longer time because it checks all the live objects. Major GC should be minimized because it will make your application unresponsive for the garbage collection duration. So if you have a responsive application and there are a lot of Major Garbage Collection happening, you will notice timeout errors.
The duration taken by garbage collector depends on the strategy used for garbage collection. That’s why it’s necessary to monitor and tune the garbage collector to avoid timeouts in the highly responsive applications.
Permanent Generation
Permanent Generation or “Perm Gen” contains the application metadata required by the JVM to describe the classes and methods used in the application. Note that Perm Gen is not part of Java Heap memory.
Perm Gen is populated by JVM at runtime based on the classes used by the application. Perm Gen also contains Java SE library classes and methods. Perm Gen objects are garbage collected in a full garbage collection.
Garbage Collection
As already mentioned in the beginning of this post, Garbage Collection is one of the prime features of the java programming language. Many people think garbage collection collects and discards dead objects. In reality,
Java garbage collection is doing the opposite! Live objects are tracked and everything
else designated garbage. As you'll see, this fundamental misunderstanding can lead
to many performance problems.
Let's start with the heap, which is the area of memory used for dynamic allocation.
In most configurations the operating system allocates the heap in advance to be
managed by the JVM while the program is running. This has a couple of important
ramifications:
- Object creation is faster because global synchronization with the operating system is not needed for every single object. An allocation simply claims some portion of a memory array and moves the offset pointer forward. The next allocation starts at this offset and claims the next portion of the array.
- When an object is no longer used, the garbage collector reclaims the underlying memory and reuses it for future object allocation. This means there is no explicit deletion and no memory is given back to the operating system.
Garbage Collection Roots
Every object tree must have one or more root objects. As long as the application can reach those roots, the whole tree is reachable. But when are those root objects considered reachable? Special objects called garbage-collection roots (GC roots; see below figure) are always reachable and so is any object that has a garbage-collection root at its own root.
There are four kinds of GC roots in Java:
Local variables are kept alive by the stack of a thread. This is not a real object virtual reference and thus is not visible. For all intents and purposes, local variables are GC roots.
Active Java threads are always considered live objects and are therefore GC roots. This is especially important for thread local variables.
Static variables are referenced by their classes. This fact makes them de facto GC roots. Classes themselves can be garbage-collected, which would remove all referenced static variables. This is of special importance when we use application servers, OSGi containers or class loaders in general.
JNI References are Java objects that the native code has created as part of a JNI call. Objects thus created are treated specially because the JVM does not know if it is being referenced by the native code or not. Such objects represent a very special form of GC root, which we will examine in more detail in the Problem Patterns section below.
Marking and Sweeping Away Garbage
To determine which objects are no longer in use, the JVM intermittently runs what is very aptly called a mark-and-sweep algorithm . As you might intuit, it's a straightforward, two-step process:
- The algorithm traverses all object references, starting with the GC roots, and marks every object found as alive.
- All of the heap memory that is not occupied by marked objects is reclaimed. It is simply marked as free, essentially swept free of unused objects.
Garbage collection is intended to remove the cause for classic memory leaks: unreachable-but-not-deleted objects in memory. However, this works only for memory leaks in the original sense. It's possible to have unused objects that are still reachable by an application because the developer simply forgot to dereference them. Such objects cannot be garbage-collected. Even worse, such a logical memory leak cannot be detected by any software. Even the best analysis software can only highlight suspicious objects.
Garbage Collection Algorithms and Performance
As I have already mentioned previously, GC event is a "Stop the World" operation where application stop its execution during this time period. Therefore, it is very important to choose a proper GC algorithm for your performance critical enterprise application. There are 5 GC algorithms available with the Oracle JVM.
Serial GC (-XX:+UseSerialGC): Serial GC uses the simple mark-sweep-compact approach for young and old generations garbage collection i.e Minor and Major GC. Serial GC is useful in client-machines such as our simple stand alone applications and machines with smaller CPU. It is good for small applications with low memory footprint.
Parallel GC (-XX:+UseParallelGC): Parallel GC is same as Serial GC except that is spawns N threads for young generation garbage collection where N is the number of CPU cores in the system. We can control the number of threads using -XX:ParallelGCThreads=n JVM option. Parallel Garbage Collector is also called throughput collector because it uses multiple CPUs to speed up the GC performance. Parallel GC uses single thread for Old Generation garbage collection.
Parallel Old GC (-XX:+UseParallelOldGC): This is same as Parallel GC except that it uses multiple threads for both Young Generation and Old Generation garbage collection.
Concurrent Mark Sweep (CMS) Collector (-XX:+UseConcMarkSweepGC): CMS Collector is also referred as concurrent low pause collector. It does the garbage collection for Old generation. CMS collector tries to minimize the pauses due to garbage collection by doing most of the garbage collection work concurrently with the application threads. CMS collector on young generation uses the same algorithm as that of the parallel collector. This garbage collector is suitable for responsive applications where we can’t afford longer pause times. We can limit the number of threads in CMS collector using -XX:ParallelCMSThreads=n JVM option.
G1 Garbage Collector (-XX:+UseG1GC): The Garbage First or G1 garbage collector is available from Java 7 and it’s long term goal is to replace the CMS collector. The G1 collector is a parallel, concurrent, and incrementally compacting low-pause garbage collector. Garbage First Collector doesn’t work like other collectors and there is no concept of Young and Old generation space. It divides the heap space into multiple equal-sized heap regions. When a garbage collection is invoked, it first collects the region with lesser live data, hence “Garbage First”. You can find more details about it at Garbage-First Collector Oracle Documentation.
That is enough for this lengthy blog post. I am planning to write several blog posts on GC tuning in the future.
Happy GC !!!
References
http://www.journaldev.com/2856/java-jvm-memory-model-and-garbage-collection-monitoring-tuning
http://www.dynatrace.com/en/javabook/how-garbage-collection-works.html
http://tutorials.jenkov.com/java-concurrency/java-memory-model.html
Code examples for beginners Beginners Lab Assignments Code Examples
ReplyDeleteThis tool http://gceasy.io is faster and easier for analyzing GC log, which might actually be worth trying.
ReplyDeleteI get a lot of great information from your blog. Thank you for your sharing this informative blog. I have bookmarked this page for my future reference.
ReplyDeleteSelenium Training in Chennai
here you explained very well about Java Garbage Collection .
ReplyDeletethanks for this nice post.
core Java online training
ReplyDeleteNice and good article. It is very useful for me to learn and understand easily. Thanks for sharing your valuable information and time. Please keep updating mulesoft online training Hyderabad
ReplyDeleteThanks for sharing this blog. We have now started providing bulk sms integration java that is very powerful and easy to integrate into your own software/application/website. As it saves your lot of valuable time in logging our interface again and again.
ReplyDelete