java memory allocation strategy

The JVM adopts a generational garbage collection strategy: the life cycles of different objects are different. At present, JVM generation is mainly divided into three generations:

  • Young Generation: All newly created objects are first allocated in the Young Generation. The new generation is divided into three areas, an Eden area, a From Survivor area and a To Sruvivor area. Most objects are allocated in the Eden area. When the Eden area is full, the surviving objects will be copied to the From Survivor area. When the From Survivor area is full, the surviving objects in this area will be copied to the To Survivor area. Finally, when the To Survivor area is also full, the objects copied from the From Survivor area and still alive will be copied to the old generation.
  • Old generation: objects that survive N times (usually 15 times) GC in the young generation will be placed in the old generation. Therefore, it can be considered that the old generation stores some objects with a long life cycle.
  • Permanent generation (implementation of method area): PermGen----->replaced with Metaspace (in local memory).
    Metaspace is the implementation of method area in HotSpot jvm. The method area is mainly used to store class information, constant pool, method data, method code, etc. The method area is logically part of the heap, but in order to distinguish it from the heap, it is usually called "non-heap".
    The essence of the metaspace is similar to that of the permanent generation, and it is the implementation of the method area in the JVM specification. But the biggest difference between the metaspace and the permanent generation is that the metaspace is not in the virtual machine, but uses local memory.

Specifically as shown in the figure below: 


Objects are first allocated in Eden

In most cases, objects are allocated in the Eden area of ​​the young generation. When the Eden area does not have enough space for allocation, the virtual machine will initiate a Minor GC.

The virtual machine provides the collection log parameter -XX:+PrintGCDetails, which tells the virtual machine to print the memory reclamation log when garbage collection occurs, and output the current allocation of memory areas when the process exits.

Use the sample code to analyze the GC process, the specific code is as follows:

package jvm;

import java.lang.String;

/**
 * Executed jvm parameters: -verbose:gc -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
 */
class GarbageCollection {
    public static final int ONEMB = 1024 * 1024;

    public static void testAllocation() {
        byte[] allocation1, allocation2, allocation3, allocation4;

        allocation1 = new byte[2 * ONEMB];
        allocation2 = new byte[2 * ONEMB];
        allocation3 = new byte[2 * ONEMB];
        allocation4 = new byte[4 * ONEMB];
        System.out.println("OK!");
    }


    public static void main(String[] args) {
        GarbageCollection.testAllocation();
    }
}

operation result:

[GC (Allocation Failure) [PSYoungGen: 6532K->1007K(9216K)] 6532K->5139K(19456K), 0.0332233 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 
OK!
Heap
 PSYoungGen      total 9216K, used 7391K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 77% used [0x00000000ff600000,0x00000000ffc3bfe0,0x00000000ffe00000)
  from space 1024K, 98% used [0x00000000ffe00000,0x00000000ffefbdf0,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 4132K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 40% used [0x00000000fec00000,0x00000000ff009200,0x00000000ff600000)
 Metaspace       used 2984K, capacity 4494K, committed 4864K, reserved 1056768K
  class space    used 325K, capacity 386K, committed 512K, reserved 1048576K

The Java heap size is limited to 20M by -Xmx20M, and the size of the new generation is limited to 10M by -Xmn, and the remaining 10M is allocated to the old generation. At the same time, the use of -XX:SurvivorRatio=8 determines that the space ratio of the new generation Eden area to a Survivor area is 8:1. You can also see "eden space 8192K, from space 1024K, to space 1024K" in the running result. The total available space for the generation is 9216K (the total capacity of the Eden area + 1 Survivor area).

A Minor GC occurs when the statement that allocates the allocation4 object in testAllocation() is executed. The result of this GC is that the new generation 6651KB becomes 148KB, and the total memory usage is almost unchanged (so the three objects of allocation1, allocation2, and allocation3 are all is alive, the virtual machine has almost nothing to recycle). The reason for this GC is that when allocating memory to allocation4, it is found that Eden has been occupied by 6MB, and the remaining space is not enough to allocate the 4M memory required by allocation4, so Minor GC occurs. During the GC, the virtual machine found that all the existing three 2MB objects could not be included in the Survivor space, so it had to be transferred to the old age in advance through the guarantee mechanism.

After this GC, the 4MB allocation4 object was successfully allocated in Eden, so the result of the program execution is that Eden occupies 4MB (occupied by allocation4), and the survivor space is occupied by 6MB (occupied by allocation1, allocation2, and allocation3). This can also be illustrated by the GC log.

Large objects go directly to the old age

The so-called large objects refer to Java objects that require a large amount of contiguous memory space. The most typical large objects are long strings and arrays. Large objects are bad news for the memory allocation of virtual machines. Often, large objects are likely to cause a lot of space in memory, and GC is triggered in advance to obtain enough contiguous space to "place" them. 
The virtual machine provides a -XX:PretenureSizeThreshold parameter, so that objects larger than this setting value are directly allocated in the old age. The purpose of this is to avoid a lot of memory copying between the Eden area and the two Survivor areas.

The sample code is as follows:

package jvm;

import java.lang.String;

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=1048576
 */
class GarbageCollection {
    public static final int ONEMB = 1024 * 1024;

    public static void testAllocation() {
        byte[] allocation4;

        allocation4 = new byte[4 * ONEMB]; // allocate directly in the old generation
        System.out.println("OK!");
    }


    public static void main(String[] args) {
        GarbageCollection.testAllocation();
    }
}

The result should be like this:

OK!
Heap
 PSYoungGen      total 9216K, used 5296K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 64% used [0x00000000ff600000,0x00000000ffb2c120,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
 PSPermGen       total 21504K, used 2554K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c7e888,0x00000000faf00000)
The actual result is:
OK!
Heap
 PSYoungGen      total 9216K, used 6697K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 81% used [0x00000000ff600000,0x00000000ffc8a450,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 10240K, used 0K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff600000)
 Metaspace       used 3063K, capacity 4494K, committed 4864K, reserved 1056768K
  class space    used 331K, capacity 386K, committed 512K, reserved 1048576K

Long-lived objects will enter the old age

Since the virtual machine adopts the idea of ​​generational collection to manage memory, it must be able to identify which objects should be placed in the new generation and which objects should be placed in the old generation when memory is recycled. To do this, the virtual machine defines an object age (Age) counter for each object. If the object is still alive after Eden's birth and the first Minor GC, and can be accommodated by the Survivor, it will be moved to the Survivor space, and the object age is set to 1. Each time the subject "survives" a Minor GC in the Survivor zone, the age increases by 1 year. When its age increases to a certain level (15 by default), it will be promoted to the old age. The threshold for the promotion of objects to the old age can be set by the parameter -XX:MaxTenuringThreshold.

space allocation guarantee

Before Minor GC occurs, the virtual machine first checks whether the maximum available continuous space in the old generation is greater than the total space of all objects in the new generation. If this condition is true, then Minor GC is safe. If not, the virtual machine checks whether the HandlePromotionFailure setting value allows guarantee failure. If it is allowed, it will continue to check whether the maximum available continuous space in the old generation is greater than the average size of objects promoted to the old generation. If it is greater, Jiang will try to perform a Minor GC, although this GC is risky. If it is less than, or the HandlePromotionFailure setting does not allow risk, then a Full GC should be performed instead. 
Let's explain what kind of risk is "taking a risk"? 

The risk here actually means that the space in the old generation may not be able to accommodate all the surviving objects in the young generation. Once it cannot accommodate, a Full GC needs to be performed.

Minor GC and Full GC

Minor GC: Reclaim memory from young generation space (including Eden and Survivor areas) to become Minor GC. When Minor GC occurs, there are two things to pay attention to:

  1. Minor GC is triggered when the JVM cannot allocate space for a new object, such as when the Eden area is full, so the higher the allocation frequency, the more frequently the Minor GC may be performed.
  2. All Minor GCs trigger "stop-the-world", stopping the application's threads. For most applications, the latency caused by stalls is negligible.

Full GC: Sort out the entire heap, including Young Generation, Old Generation, and Permanent Generation. Full GC is slower than Minor GC because it needs to reclaim the entire area, so the number of Full GC should be reduced as much as possible.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325947837&siteId=291194637