This is the 14th day of my participation in the August Text Challenge.More challenges in August

1. The object is allocated in Eden first – The object is allocated in Eden+ large objects in the old age

In most cases, objects are allocated in the Eden region of the new generation. When the Eden area does not have enough space to allocate, the virtual machine will initiate a Minor GC. The HotSpot VIRTUAL machine provides the -xx: +PrintGCDetails collector log parameter, which tells the virtual machine to print the memory reclaim log when garbage collection occurs and the current memory area allocation when the process exits. In practical troubleshooting, collector logs are often printed to files and analyzed by tools.

Next, in the following code fragment testAllocation() method, we try to allocate three 2MB objects and one 4MB object. At runtime, we limit the Java heap size to 20MB by using -xMS20m, -xmx20m, and -xmn10m. Of this, 10MB is allocated to the new generation and the remaining 10MB to the old generation. -xx: Survivor Ratio=8 determines that the space Ratio between Eden area and a Survivor area in the Cenozoic era is 8:1

/** * VM parameters:  -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC * Here we manually specify the garbage collector to collect memory for Serial+Serial Old collector combinations in client mode. * Due to the different collection mechanisms of different collectors, we need to manually specify Serial+Serial Old mode here in order to present the guarantee effect of memory allocation. * /
public class Test {
    private static final int _1MB = 1024 * 1024;
    public static void testAllocation(a) {
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB]; // A Minor GC occurs
    }

    public static void main(String[] args) { testAllocation(); }}Copy the code

Output the print result:

[GC (Allocation Failure) [DefNew: 7808K->603K(9216K), 0.0062181 secs] 7808K->6747K(19456K), 0.0074891 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 4781K [0x00000000fec00000.0x00000000ff600000.0x00000000ff600000)
  eden space 8192K,  51% used [0x00000000fec00000.0x00000000ff014930.0x00000000ff400000)
  from space 1024K,  58% used [0x00000000ff500000.0x00000000ff596d10.0x00000000ff600000)
  to   space 1024K,   0% used [0x00000000ff400000.0x00000000ff400000.0x00000000ff500000)
 tenured generation   total 10240K, used 6144K [0x00000000ff600000.0x0000000100000000.0x0000000100000000)
   the space 10240K,  60% used [0x00000000ff600000.0x00000000ffc00030.0x00000000ffc00200.0x0000000100000000)
 Metaspace       used 3199K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 346K.capacity 388K.committed 512K.reserved 1048576K
Copy the code

The output results also clearly show the information of “Eden Space 8192K, from Space 1024K, to Space 1024K”. The total available space of the new generation is 9216KB (the total capacity of Eden area +1 Survivor area).

The current three objects are allocated into memory as follows :(what happens when allocation4 is allocated?)

A Minor GC occurs when the statement in testAllocation() allocates the allocation4 object. The result of this GC is that the new generation 7808KB becomes 603KB. The total memory footprint was barely reduced (since allocation1, 2, and 3 objects were all alive, the virtual machine found few recyclable objects).

The reason for this garbage collection is that when allocating memory for Allocation4, it was discovered that Eden had already been occupied by 6MB, and the remaining space was not enough to allocate the 4MB required for Allocation4, so a Minor GC occurred. During garbage collection, the virtual machine found that three existing 2MB objects could not fit into the Survivor space (the Survivor space was only 1MB in size), so it had to be moved to the old age through allocation guarantee mechanism.

After this collection, the 4MB allocation4 object is successfully allocated in Eden. Therefore, after the program is executed, Eden is occupied by 4MB (occupied by Allocation4), Survivor is idle, and the old age is occupied by 6MB (occupied by Allocation1, 2, and 3). This can be verified by GC logs. (Tenured Generation Total 10240K, used 6144K)

2 large objects directly into the old age

A large object is a Java object that requires a large amount of contiguous memory. The most typical large object is a long string or an array with a large number of elements. The byte[] array in this example is a typical large object. Large objects are bad news for virtual machine memory allocation. The only thing worse than a large object is a group of short-lived large objects, which should be avoided when writing programs.

The reason to avoid large objects in the Java VIRTUAL machine is that when allocating space, it can cause garbage collection to be triggered in advance when there is plenty of memory available, in order to obtain enough contiguous space to place them, and when copying objects, large objects can mean high memory copying overhead. Imagine a large object that has repeatedly missed GC and has to replicate in two Survivor zones to get to the old age, which is time-consuming and efficient in itself.

The HotSpot VIRTUAL machine provides the -xx: PretenureSizeThreshold parameter, which specifies that objects larger than this value will be allocated directly in the older generation. This is to avoid replication between Eden and two Survivor zones, resulting in a large number of memory replication operations.

Example code is as follows:

/** * VM parameters:  -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC * - XX: PretenureSizeThreshold = 3145728 this parameter cannot be directly with such parameters as the -xmx write 3 MB, need to convert byte * /
public class Test2 {
    private static final int _1MB = 1024 * 1024;

    public static void testAllocation(a) {
        byte[] allocation;
        allocation = new byte[4 * _1MB];
    }

    public static void main(String[] args) { testAllocation(); }}Copy the code

Output result:

Heap
 def new generation   total 9216K, used 1857K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  eden space 8192K,  22% used [0x00000000fec00000, 0x00000000fedd0750, 0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
 tenured generation   total 10240K, used 4096K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
   the space 10240K,  40% used [0x00000000ff600000, 0x00000000ffa00010, 0x00000000ffa00200, 0x0000000100000000)
 Metaspace       used 3223K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 350K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0
Copy the code

According to the printed result, 22% of the Eden area used in the new generation, which is 1802KB, is not the corresponding allocation object. Tenured Generation total 10240K, used 4096K, the space 10240K, 40% used 4MB, which exactly matches the size of the allocation object. This verifies that when our large object exceeds 3MB, it goes straight to the old age.

Note that the -xx: PretenureSizeThreshold parameter is only valid for Serial and ParNew censors, and not supported by HotSpot censors such as the Parallel Insane. If you must use this parameter for tuning, consider a collector combination of ParNew plus CMS.

summary

In this article we will start with two basic object assignments, and in the next article we will continue with how long-lived objects age and dynamic age judgments.