一、 Java (JVM) Memory Model - Heap
PermGen memory space has been removed in Java 8.
1.1 Memory Management in Java – Young Generation
The young generation is the place where all the new objects are created. When the young generation is filled, garbage collection is performed. This garbage collection is called Minor GC. Young Generation is divided into three parts – Eden Memory and two Survivor Memory spaces.
Important Points about Young Generation Spaces:
- 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.
1.2 Memory Management in Java – 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 a longer time.
1.3 Java Heap Memory Switches
- -Xms: For setting the initial heap size when JVM starts
- -Xmx: For setting the maximum heap size.
- -Xmn: For setting the size of the Young Generation, rest of the space goes for Old Generation.
- -XX:NewRatio: Set the size of the Young Generation as a ratio of the size of the Old Generation.
- -XX:SurvivorRatio: Set the size of Eden space relative to the size of a survivor space.
1.4 PermGen VS Metaspace
The JDK 8 HotSpot JVM is now using native memory for the representation of class metadata and is called Metaspace; similar to the Oracle JRockit and IBM JVM’s.
The good news is that it means no more java.lang.OutOfMemoryError: PermGen space problems and no need for you to tune and monitor this memory space anymore…not so fast. While this change is invisible by default, we will show you next that you will still need to worry about the class metadata memory footprint. Please also keep in mind that this new feature does not magically eliminate class and classloader memory leaks. You will need to track down these problems using a different approach and by learning the new naming convention.
PermGen space situation:
- This memory space is completely removed.
- The PermSize and MaxPermSize JVM arguments are ignored and a warning is issued if present at start-up.
Metaspace memory allocation model:
-
Most allocations for the class metadata are now allocated out of
native memory. -
The klasses that were used to describe class metadata have been
removed.
Metaspace capacity:
-
By default class metadata allocation is limited by the amount of
available native memory (capacity will of course depend if you use a
32-bit JVM vs. 64-bit along with OS virtual memory availability). -
A new flag is available (MaxMetaspaceSize), allowing you to limit the amount of native memory used for class metadata. If you don’t specify this flag, the Metaspace will dynamically re-size depending of the application demand at runtime.
Metaspace garbage collection:
-
Garbage collection of the dead classes and classloaders is triggered
once the class metadata usage reaches the “MaxMetaspaceSize”. -
Proper monitoring & tuning of the Metaspace will obviously be
required in order to limit the frequency or delay of such garbage
collections. Excessive Metaspace garbage collections may be a symptom of classes, classloaders memory leak or inadequate sizing for your application.
Java heap space impact:
- Some miscellaneous data has been moved to the Java heap space. This means you may observe an increase of the Java heap space following a future JDK 8 upgrade.
Metaspace monitoring:
-
Metaspace usage is available from the HotSpot 1.8 verbose GC log
output. -
Jstat & JVisualVM have not been updated at this point based on our
testing with b75 and the old PermGen space references are still
present.
二、Java Garbage Collection
Java Garbage Collection is the process to identify and remove the unused objects from the memory and free space to be allocated to objects created in future processing. One of the best features of Java programming language is the automatic garbage collection, unlike other programming languages such as C where memory allocation and deallocation is a manual process.
Garbage Collector is the program running in the background that looks into all the objects in the memory and find out objects that are not referenced by any part of the program. All these unreferenced objects are deleted and space is reclaimed for allocation to other objects.
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 a long 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.
Also, there is Full GC, which means cleaning the entire Heap – both Young and older generation spaces.
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.
One of the basic ways of garbage collection involves three steps:
- Marking: This is the first step where garbage collector identifies
which objects are in use and which ones are not in use. - Normal Deletion: Garbage Collector removes the unused objects and
reclaim the free space to be allocated to other objects. - Deletion with Compacting: For better performance, after deleting
unused objects, all the survived objects can be moved to be
together. This will increase the performance of allocation of memory
to newer objects.
There are two problems with a simple mark and delete approach.
- First one is that it’s not efficient because most of the newly
created objects will become unused - Secondly objects that are in-use for multiple garbage collection
cycle are most likely to be in-use for future cycles too.
The above shortcomings with the simple approach is the reason that Java Garbage Collection is Generational and we have Young Generation and Old Generation spaces in the heap memory.
2.1 Garbage Collection Process
- When an object is created, it is allocated on the Eden(1) space.
Because the Eden space is not that big, it gets full quite fast. The
garbage collector runs on the Eden space and marks objects as alive. - Once an object survives a garbage collecting process, it gets moved
into a so-called survivor space S0(2). The second time the garbage
collector runs on the Eden space, it moves all surviving objects
into the S1(3) space. Also, everything that is currently on S0(2) is
moved into the S1(3) space. - If an object survives for X rounds of garbage collection (X depends
on the JVM implementation, in my case it’s 8), it is most likely
that it will survive forever, and it gets moved into the Old(4)
space.
Taking everything said so far, if you look at the garbage collector graph(6), each time it has run, you can see that the objects switch to the survivor space and that the Eden space gained space. And so on and so forth. The old generation can be also garbage collected, but since it is a bigger part of the memory compared to Eden space, it does not happen that often. The Metaspace(5) is used to store the metadata about your loaded classes in the JVM.
The presented picture is actually a Java 8 application. Prior to Java 8, the structure of the memory was a bit different. The metaspace is called actually the PermGen. space. For example, in Java 6, this space also stored the memory for the string pool. Therefore, if you have too many strings in your Java 6 application, it might crash.
2.2 GC Types
Type | Target | Trigger | Impact |
---|---|---|---|
Minor GC | Young generation | Eden getting full | No effect on latency* |
Major GC | Old generation | Minor GC fails | Can have latency effect |
Full GC | Whole heap + MetaSpace | Minor or Major GC fail | Can have latency effect |
2.3 GC Algorithms
Type | Threads | Algorythm | Effect | Default in |
---|---|---|---|---|
Serial | YoungGen: single OldGen: single | YoungGen: mark and copy OldGen: mark sweep compact | Stop the world every case | Java6 if client class* |
Parallel | YoungGen: multi OldGen: single | YoungGen: mark and copy OldGen: mark sweep compact | Stop the world when OldGen cleanup is necessary | Java6 if server class** |
ParalellOld | YoungGen: multi OldGen: multi | YoungGen: mark and copy OldGen: mark summary compact | No stop the world, but allocation is not so efficient | non default |
Concurrent Mark Sweep | YoungGen: multi OldGen: multi | YoungGen: mark and copy OldGen: mark compact | No stop the world, but allocation is not so efficient | non default |
G1 GC | YoungGen: multi OldGen: multi | memory split into chunks and marked as usable or "under maintenance"which is cleaned by the GC currently | Best of both, the promise ofless GC pauses, more predictable GC runs | Java9 |
-
Serial GC – This is the simplest garbage collector, designed for single threaded systems and small heap size. It freezes all applications while working. Can be turned on using -XX:+UseSerialGC JVM option.
-
Parallel/Throughput GC – This is JVM’s default collector in JDK 8. As the name suggests, it uses multiple threads to scan through the heap space and perform compaction. A drawback of this collector is that it pauses the application threads while performing minor or full GC.
It is best suited if applications that can handle such pauses, and try to optimize CPU overhead caused by the collector. It can be enabled by explicitly specifying the option: -XX:+UseParallelGC -
Mostly concurrent GC – If you remember, earlier in this article, it was mentioned that the garbage collecting process is actually pretty expensive, and when it runs, all thread are paused. However, we have this mostly concurrent GC type, which states that it works concurrent to the application. However, there is a reason why it is “mostly” concurrent. It does not work 100% concurrently to the application. There is a period of time for which the threads are paused. Still, the pause is kept as short as possible to achieve the best GC performance. Actually, there are 2 types of mostly concurrent GCs:
-
Garbage First – high throughput with a reasonable application pause time. Enabled with the option: -XX:+UseG1GC
-
Concurrent Mark Sweep – The application pause time is kept to a minimum. It can be used by specifying the option: -XX:+UseConcMarkSweepGC. As of JDK 9, this GC type is deprecated.
-
2.3 GC Roots
The so-called GC (Garbage Collector) roots are objects special for garbage collector. Garbage collector collects those objects that are not GC roots and are not accessible by references from GC roots.
There are several kinds of GC roots. One object can belong to more than one kind of root. The root kinds are:
Class - class loaded by system class loader. Such classes can never be unloaded. They can hold objects via static fields. Please note that classes loaded by custom class loaders are not roots, unless corresponding instances of java.lang.Class happen to be roots of other kind(s).
Thread - live thread
Stack Local - local variable or parameter of Java method
JNI Local - local variable or parameter of JNI method
JNI Global - global JNI reference
Monitor Used - objects used as a monitor for synchronization
Held by JVM - objects held from garbage collection by JVM for its purposes. Actually the list of such objects depends on JVM implementation. Possible known cases are: the system class loader, a few important exception classes which the JVM knows about, a few pre-allocated objects for exception handling, and custom class loaders when they are in the process of loading classes. Unfortunately, JVM provides absolutely no additional detail for such objects. Thus it is up to the analyst to decide to which case a certain “Held by JVM” belongs.
参考:
《Java Virtual Machine (JVM) & its Architecture》
《Java Memory Management》
《Java (JVM) Memory Model – Memory Management in Java》
《Java Garbage Collection Basics》
《Java Version Upgrades: GC Overview》
《PermGen Elimination project is promoting》