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

Most collectors in the HotSpot VIRTUAL machine use generational collection to manage heap memory, so memory collection must be able to decide which live objects should be placed in the new generation and which should be placed in the old generation. To do this, the virtual machine defines an object Age counter for each object, which is stored in the object header. An object is usually born in Eden, and if it survives after the first Minor GC and can be accommodated by Survivor, it is moved to Survivor and its object age is set to 1. Each time an object survives a Minor GC in a Survivor zone, its age increases by one year, and when it reaches a certain age (15 by default), it is promoted to the old age. The age threshold for the object to be promoted to the old age can be set with the parameter ** -xx: MaxTenuringThreshold**.

1. MaxTenuringThreshold = 1

When we set the memory parameter with -xx: MaxTenuringThreshold=1, we execute the following code:

/ * * *@Des: Long-lived objects into the old age test * VM parameters:  -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC * - XX: MaxTenuringThreshold = 1: when a new object to enter old age reaches 1 s * - XX: + PrintTenuringDistribution: the JVM in each new generation of GC, print out the survival area of the age distribution of the object. * /
public class TestLongObjToOld {
    private static final int _1MB = 1024 * 1024;

    public static void testTenuringThreshold(a) {
        byte[] allocation1, allocation2, allocation3;
        allocation1 = new byte[_1MB / 4]; // When to enter the old age depends on XX:MaxTenuringThreshold setting
        allocation2 = new byte[4 * _1MB]; //4048KB
        allocation3 = new byte[4 * _1MB];//4048KB Eden occupies 8352KB
        allocation3 = null;  // Disconnect the reference and become a garbage object
        allocation3 = new byte[4 * _1MB]; // Minor GC is triggered when 4MB of memory is allocated
    }

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

Output result:

[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     896768 bytes,     896768 total
: 6079K->875K(9216K), 0.0036167 secs] 6079K->4971K(19456K), 0.0036538 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:        584 bytes,        584 total
: 5056K->0K(9216K), 0.0008881 secs] 9152K->4968K(19456K), 0.0009051 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4316K [0x00000000fec00000.0x00000000ff600000.0x00000000ff600000)
  eden space 8192K,  52% used [0x00000000fec00000.0x00000000ff037058.0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000.0x00000000ff400248.0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000.0x00000000ff500000.0x00000000ff600000)
 tenured generation   total 10240K, used 4968K [0x00000000ff600000.0x0000000100000000.0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000.0x00000000ffada120.0x00000000ffada200.0x0000000100000000)
 Metaspace       used 3244K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 353K.capacity 388K.committed 512K.reserved 1048576K
Copy the code

When allocation1 and Allocation2 objects are loaded, the combined value of the two objects is 4.25MB, which can be stored in Eden area (Eden area size 9216K) without any problem. The memory diagram is as follows:

When the allocation3 object is created and the Eden space is found to be insufficient, the first GC will be triggered:

Let’s look at the first GC print:

[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     896768 bytes,     896768 total
: 6079K->875K(9216K), 0.0036167 secs] 6079K->4971K(19456K), 0.0036538 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Copy the code

GC: indicates that a garbage collection has occurred, without the Full modifier, indicating that a Minor GC has occurred;

Allocation Failure: Indicates that the GC is caused because there is not enough space in the young generation to store new data.

6079K->875K(9216K) The following parameters are the used capacity of the memory region before GC, the used capacity of the memory region after GC, and the total capacity of the memory region.

6079K->4971K(19456K) The three parameters are: the size of heap before garbage collection, the size of heap after garbage collection, and the total size of heap.

0.0036167 SECS: indicates the GC duration of the current generation

So the conclusion we draw from this log is:

  • The Cenozoic GC decreased by 6079-875 = 5204KB
  • The total Heap area decreased by 6079-4971 = 1108KB
  • 5204KB – 1108KB = 4096KB indicates that a total of 4096KB objects are transferred from the young generation to the old generation

Ok, let’s visualize it by drawing a picture:

Please pay attention here: Since both our allocation1 object and allocation2 object are strong references and will not be recycled, they will definitely go directly into the survivor area. Allocation1 object can go into the survivor area, but our allocation2 object is too large to go into S1. Therefore, according to the garbage collector’s default guarantee mechanism described above, allocation2 objects will be stored directly into our old age. This also explains why objects with a size of 4096K (4MB) ended up in the old age

After the first GC, Eden will have enough space to store allocation3 objects.

Let’s look at the second GC:

allocation3 = null// Once this line of code is executed, our allotion3 object has no direct referrers
Copy the code

As follows:

Then the last line of code executes:

allocation3 = new byte[4 * _1MB]; // Minor GC is triggered when 4MB of memory is allocated
Copy the code

If we continue to apply for allocating objects of 4MB size to Eden area this time, GC will still be triggered due to insufficient allocation, and we will continue to analyze the following logs:

[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:        584 bytes,        584 total
: 5056K->0K(9216K), 0.0008881 secs] 9152K->4968K(19456K), 0.0009051 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Copy the code

So the conclusion we draw from this log is:

  • The Cenozoic GC decreased by 5056-0 = 5056KB
    • Note: The new generation has been reduced to 0! Represents all the objects in S1 have been moved to the old age! (Because our age threshold is set to 1) The allocation3 object is recycled directly
  • The total Heap area decreased by 9152-4968 = 4184KB
    • The main thing is that our Allocation3 object and a few system objects are recycled
  • 5056KB – 4184KB =872 KB Indicates that 872KB objects are transferred from the young generation to the old generation

The final memory result is as follows:

It matches our last memory log:

Heap
 def new generation   total 9216K, used 4316K [0x00000000fec00000.0x00000000ff600000.0x00000000ff600000)
  eden space 8192K,  52% used [0x00000000fec00000.0x00000000ff037058.0x00000000ff400000)
  from space 1024K,   0% used [0x00000000ff400000.0x00000000ff400248.0x00000000ff500000)
  to   space 1024K,   0% used [0x00000000ff500000.0x00000000ff500000.0x00000000ff600000)
 tenured generation   total 10240K, used 4968K [0x00000000ff600000.0x0000000100000000.0x0000000100000000)
   the space 10240K,  48% used [0x00000000ff600000.0x00000000ffada120.0x00000000ffada200.0x0000000100000000)
Copy the code

2. MaxTenuringThreshold = 15

The code has not changed, only the JVM parameters have changed, we can directly see the log results after the run:

[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     877184 bytes,     877184 total
: 6079K->856K(9216K), 0.0025954 secs] 6079K->4952K(19456K), 0.0026263 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   1:        728 bytes,        728 total
: 5037K->0K(9216K), 0.0009100 secs] 9133K->4949K(19456K), 0.0009267 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Copy the code

Note: the amazing thing we found is that after the second GC, the Cenozoic footprint becomes 0! What the hell is this?! We clearly set the threshold at 15

There is another rule with this object age that allows objects to age without waiting 15 GC.

So what are the rules? I think some of you have already guessed that this is our dynamic age rule, and in the next article we will continue to show you how dynamic age rules and JVM space allocation guarantees work.