JVM runtime memory is also known as Heap memory. The Java heap can also be subdivided From a GC perspective into the new generation (Eden, From Survivor, and To Survivor) and the old generation.
Modern mainstream VIRTUAL machine (Hotspot VM) garbage collection adopts “generational collection” algorithm. Generational collection is based on the fact that objects have different life cycles, so different collection methods can be adopted for objects with different life cycles to improve collection efficiency. The idea of “generational” is embodied in Hotspot VM, which divides memory into different physical areas.
An object goes from birth to death
After an object is generated, it will be allocated on the stack first. If it is not allocated on the stack, it will enter Eden area. Eden area will enter Surivivor area after garbage collection, and survivor area will enter another survivor area after garbage collection. At the same time, some objects in the Eden region also enter another survivot, and when they are old enough, they enter the old region, which is a logical movement of the whole object.
1. Young Generation
Mainly used to store new objects. It takes up about a third of the heap. Because objects are created frequently, MinorGC is frequently triggered for garbage collection by the new generation. The Cenozoic era is divided into three regions: Eden, ServivorFrom and ServivorTo.
- Eden: Birthplace of a new Java object (if the newly created object takes up a lot of memory, it is allocated directly to the old age). When Eden runs out of memory, MinorGC is triggered to do a garbage collection for the new generation
- ServivorTo: Saved a MinorGC survivor
- ServivorFrom: Survivor of the last GC, as scanned in this GC
MinorGC process
- MinorGC uses a copy algorithm
- First, copy the living objects in Eden and ServivorFrom to the ServicorTo region (if any objects are old and reach the old age criteria, then copy them to the old age area), and at the same time copy the age of these objects +1 (if ServicorTo space is not enough, put them into the old age area).
- Then empty the objects in Eden and ServicorFrom
- Finally, the ServicorTo and ServicorFrom are swapped, and the old ServicorTo becomes the ServicorFrom section for the next GC
Why can’t there be zero Survivor partitions?
If Survivor is 0, that is, there is only one Eden partition in the new generation. After each garbage collection, the surviving objects will enter the old generation, so that the memory space of the old generation will be filled up quickly, triggering the most time-consuming Full GC. Obviously, the efficiency of such a collector is totally unacceptable.
Why can’t there be 1 Survivor partition?
If there is one Survivor partition, and if we divide the two regions 1:1, then half of the memory space is idle at any given time, obviously too little space utilization is not the best solution.
But if you set the ratio of memory space to 8:2, it just seems “fine”, assume that the new generation of memory is 100 MB (Survivor size is 20 MB), and now after 70 MB objects are garbage collected, the remaining active objects are 15 MB into Survivor. At this point, the new generation is down to 5 MB of available memory, which quickly leads to garbage collection. Obviously, the biggest problem with this garbage collector is that it requires frequent garbage collection.
Why are there 2 Survivor partitions?
If the Survivor partition has two partitions, we can set the memory ratio of Eden, From Survivor, and To Survivor To 8:1:1, so that the new generation memory utilization is 90% at any time, which is basically as expected. In addition, most objects of the virtual machine are in line with the “instant death” feature, so every new object is generated in Eden area with a large space proportion. After garbage collection, the surviving object method is stored in Survivor area. If it is the surviving object in Survivor area, the “age” is +1. When age reaches 15 (set by -xx :+MaxTenuringThreshold) the object is upgraded to the old generation.
conclusion
According to the above analysis, when there are two Survivor partitions of the new generation, both the space utilization rate and the efficiency of program running are optimal, so this is also the reason why there are two Survivor partitions.
2. Old Generation
It mainly stores memory objects with long lifetime in application programs. Older objects are more stable, so MajorGC is not executed very often. Before MajorGC is generally carried out a MinorGC, so that there is a new generation of objects into the old age, resulting in space is not enough time to trigger. MajorGC is also triggered early for garbage collection to free up space when a large contiguous space cannot be found for a newly created larger object.
MajorGC process
MajorGC uses a mark-sweep algorithm. First scan all the ages once, mark the surviving objects, and then recycle the unmarked objects. MajorGC takes a long time because it is scanned and recycled. MajorGC generates memory fragmentation, and in order to reduce memory consumption, we usually need to merge or mark it for direct allocation next time. An OOM (Out of Memory) exception is raised when the old age is too full.
3 Perm Generation
A permanent area of memory that stores metadata, such as Class and Method metadata, and has little to do with the Java objects to be collected by garbage collection. Compared with the new generation and the old generation, the division of this region has less impact on garbage recycling. GC does not clean up the permanent area during the main program runtime, so this also causes the permanent generation area to swell as more classes are loaded, eventually throwing an OOM exception.
JAVA8 and metadata
In Java8, the persistent generation has been removed and replaced by an area called the “metadata area” (meta space). Similar in nature to permanent generations, meta-spaces are implementations of method areas in the JVM specification. However, the biggest difference between a meta-space and a permanent generation is that the meta-space is not in the virtual machine, but uses local memory. Therefore, by default, the size of the meta-space is limited only by local memory. The metadata of the class is put into Native Memory, and the string pool and static variables of the class are put into the Java heap, so that the amount of metadata of the class can be loaded is not controlled by MaxPermSize, but by the actual available space of the system.
4 Memory allocation policy
Common heap memory allocation tests are as follows:
- Objects are allocated in Eden area first
- Big object goes straight to the old age
- Long-lived objects will enter the old age
parameter | That information |
---|---|
-Xms | Initial heap size. Such as: – Xms256m |
-Xmx | Maximum heap size. Such as: – Xmx512m |
-Xmn | Cenozoic size. Usually 1/3 or 1/4 of Xmx. New generation =Eden+2 Survivor Spaces. The actual available space is =Eden+1 Survivor, i.e. 90% |
-Xss | JDK1.5+ stack size is 1M per thread, and generally 1M is sufficient if the stack is not very deep |
-XX:NewRatio | The ratio of the new generation to the old. If — XX:NewRatio=2, the Cenozoic generation occupies 1/3 of the whole heap space, and the old generation occupies 2/3 |
-XX:SurvivorRatio | Ratio of Eden to Survivor in the New generation. The default value is 8, meaning Eden accounts for 8/10 of the new generation space and the other two Survivor accounts for 1/10 each |
-XX:PermSize | The initial size of the permanent generation (method area) |
-XX:MaxPermSize | The maximum value of the permanent generation (method area) |
-XX:+PrintGCDetails | Printing GC Information |
-XX:+HeapDumpOnOutOfMemoryError | Let the virtual machine Dump a snapshot of the current memory heap Dump for analysis when a memory overflow occurs |
Parametric basic strategy
The size of each partition has a significant impact on GC performance. How to adjust the size of each partition to the appropriate size, analyzing the size of active data is a good starting point.
Size of active data: The size of the heap occupied by long-lived objects when the application is running stably, that is, the size of the heap occupied by older generations after Full GC.
It can be obtained from the data size of the old years after Full GC in the GC log. A more accurate method is to obtain GC data for several times after the program is stable and calculate the size of active data by taking the average value. The ratio between active data and partitions is as follows:
space | A multiple |
---|---|
Total size | 3-4Times the size of the active data |
The new generation | 1-1.5Size of active data |
The old s | 2-3Times the size of the active data |
The permanent generation | 1.2-1.5Permanent generation space occupied after doubling Full GC |
For example, if the active data size from the old GC log is 300M, the partition size could be set to:
Total heap: 1200MB = 300MB × 4
New generation: 450MB = 300MB x 1.5
Old: 750MB = 1200MB-450MB
These Settings are only initial values for the heap size, which may be adjusted in later optimizations, depending on the characteristics and requirements of the application.