Hello hello monkeys, I’m a turnip for coding bugs. Public account: Big Turnip Technology. Original is not easy, but welcome to reprint.

Previous article: JVM- Dynamic Age Determination introduced four ways in which objects age

  • The big object
  • Dynamic age judgment
  • After minor GC, survivor space cannot hold all surviving objects
  • The age threshold of a viable object has been reached. Procedure Such as 15

Next, we’ll verify these four approaches in code.

Review the knowledge

Before we get started, let’s review the facts:

0.134: [Allocation Failure (GC) 0.134: [ParNew: [Times: user=0.00 sys=0.00, real= 0.001secs] [Times: user=0.00 sys=0.00, real= 0.001secs]Copy the code

As it relates to viewing GC logs, I will briefly describe the general meaning of logs:

  • 0.134. This means how long after the program starts, the garbage collection takes place. Unit: second
  • The GC (Allocation Failure). GC means garbage collection has taken place, which goes without saying. Then the Allocation Failure in parentheses is the cause of garbage collection. In this case, Allocation Failure refers to garbage collection caused by space Allocation Failure.
  • ParNew:ParNew is the garbage collector used by the new generation garbage collection, which also represents a minor gc.
  • 7444 k – > 685 k (9216 k), 0.0011650 secs). Next, 7444K, is the space used by the new generation before recycling. 685K is the space already used by the new generation of garbage recycling. 9216K is the total space of Cenozoic era. 0.0011650 secs is the time required for garbage collection.
  • 7444K->685K(19456K), 0.0012583 secs], 7444K is the space used before the whole heap (new generation + old age) garbage collection. 685K is the space that has been used after the entire heap has been collected. 19456K is the total space of the entire heap.
  • [Times: user=0.00 sys=0.00, real= 0.01secs] That’s how long the GC took, because it kept the 2 as a decimal, so it was rounded to 0.

Ok, almost, start directly !!!!

The big object

First, a quick review.

Books, too, don’t say how big an object is, it’s abstract.

Let’s be more specific here:

-XX:PretenureSizeThreshold=3m

An object larger than 3m is a large object.

-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m

Here, we give the new generation 10 meters, 20 meters.

In other words, the old space =20m-10m=10m

-XX:SurvivorRatio=8

Eden: S0: S1 equals 8:1:1

Therefore, the Eden area is 8m, S0 is 1M, and S1 is 1M

The garbage collectors used are ParNew and CMS

-XX:+UseParNewGC -XX:+UseConcMarkSweepGC

Print the GC details

-XX:+PrintGCDetails

Prints the GC timestamp

-XX:+PrintGCTimeStamps

Where are these GC logs? Files, of course.

-Xloggc:bigobject.log

Ok, the JVM configuration is almost ready, directly to the code.

JVM configuration Parameters

-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m -XX:MaxHeapSize=20m -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3m -XX:MaxTenuringThreshold=15 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:bigobject.log
Copy the code

Code:

byte[] array1 = new byte[2*_1MB];

byte[] array2 = new byte[3*_1MB];

Because the object 2m is not a large object, it is allocated to the Eden area. The objects of 3M are large and therefore allocated to the old section.

All of this is what we’re guessing based on theory.

Next, we run the code and query the log file bigObject.log

Java HotSpot(TM) 64-bit Server VM (25.261-B12) for BSD-AMD64 JRE (1.8.0_261-B12) Built on Jun 18 2020 06:38:55 by "java_re" with GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5) Memory: 4k page, physical 33554432k(382392k free) /proc/meminfo: CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC Heap par new generation total 9216K, used 4849K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000) eden space 8192K, 59% used [0x00000007bec00000, 0x00000007bf0bc580, 0x00000007bf400000) from space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000) to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000) concurrent mark-sweep generation total 10240K, used 3072K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) Metaspace used 3074K, capacity 4496K, committed 4864K, reserved 1056768K class space used 337K, capacity 388K, committed 512K, reserved 1048576KCopy the code

Now, let’s analyze the logs to see if our guess is correct.

CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=3145728 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+UseParNewGC Heap

These are the JVM parameters that we set ourselves, and some systems add for us. Such as: – XX: XX: + UseCompressedClassPointers – + UseCompressedOops

That’s not the point. You just need to know.

The point is this:

Heap  
 par new generation   total 9216K, used 4849K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  eden space 8192K,  59% used [0x00000007bec00000, 0x00000007bf0bc580, 0x00000007bf400000)
  from space 1024K,   0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
  to   space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
 concurrent mark-sweep generation total 10240K, used 3072K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
Copy the code

The heap after GC is collected. I want to emphasize it here.

Par New Generation total 9216K, used 4849K represents the new generation. Now the total space size is 9216K, and the used space size is 4849K.

In fact, we know that 2M objects enter Eden area, but now 4849K is obviously greater than 2048K (2M).

What does that tell us?

In addition to loading our own written objects, the JVM also loads some unknown objects. Unknown objects, which are mostly generated by the JVM itself, should be ignored for now

Back to the main question: will big objects go straight to the old days?

Let’s take a look at this log:

concurrent mark-sweep generation total 10240K, used 3072K

The total size of the old space is 10240K, 3072K has been used.

See here, in fact, I believe you can already understand. We define a large object as an object of 3m or greater. The logs also tell us that 3072K, or 3m, of the old age has been used.

So, we’ve already verified this with code: large objects go straight into the old age.

Dynamic age judgment

First of all, what is dynamic age judgment?

The book explains:

To sum up: that is to say, if the size of all objects of the same age occupies more than half of the survivor space in the survivor area, objects older than or equal to this age can directly enter the old age.

That’s what the books say.

But that’s not true.

The correct statement is: in a survivor zone, the sum of all age objects in a survivor zone is greater than half of the survivor space, and any object older than or equal to that age can enter the old age.

Let’s go straight to the code and JVM configuration parameters:

JVM configuration parameters:

-XX:NewSize=10m -XX:MaxNewSize=10m -XX:InitialHeapSize=20m -XX:MaxHeapSize=20m -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=10m -XX:MaxTenuringThreshold=15 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:dynamicheck.log
Copy the code

Code:

Let’s run the code directly and then look directly at the log file:

Java HotSpot(TM) 64-bit Server VM (25.281-B09) for BSD-AMD64 JRE (1.8.0_281-B09) Built on Dec 9 2020 12:44:49 by "java_re" with GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5) Memory: built on Dec 9 2020 12:44:49 by "java_re" with GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5) Memory 4k page, physical 16777216k(106332k free) /proc/meminfo: CommandLine flags: -XX:InitialHeapSize=20971520 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=10485760 -XX:MaxTenuringThreshold=15 -XX:NewSize=10485760 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -xx :+UseConcMarkSweepGC -xx :+UseParNewGC 0.117: [Allocation Failure (GC) 0.118: [ParNew: 7115K->619K(9216K), 0.0029447 secs] 7115K->619K(19456K), 0.0033789 secs [Times: Sys =0.00, real=0.00 secs] 0.122: [Allocation Failure (GC) 0.122: [ParNew: 7223K->0K(9216K), 0.0020159secs] 7223K->0K(9216K), 0.0020159secs] [Times: User =0.01 sys=0.00, real=0.00 SECS] Heap PAR new Generation Total 9216K, used 2212K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000) eden space 8192K, 27% used [0x00000007bec00000, 0x00000007bee290e0, 0x00000007bf400000) from space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000) to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000) concurrent mark-sweep generation total 10240K, used 601K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000) Metaspace used 2713K, capacity 4486K, committed 4864K, reserved 1056768K class space used 291K, capacity 386K, committed 512K, reserved 1048576KCopy the code

Some code analysis:

We now allocate three 2M objects in a row, and one 300K object, and set array1 to NULL. The heap diagram should look something like this

Next, we need to allocate another 2M object. At this time, can Eden continue to allocate space?

Definitely not, because the Eden area is only 8M.

At this point, the young GC is executed to clean up the space.

Next, let’s look at the log file:

ParNew: 7115K->619K(9216K)

Note Before GG, 7115K is occupied, including three 2M objects, 300K objects, and hundreds of UNKNOWN objects. After GC, only 619K objects are left, including 300K objects and unknown objects.

The newly created 2m object is then allocated to the Eden area

At this point, we have to observe an important point: that is, the FROM region is 1m, which is 1024K. Now, there are 619K objects in the FROM area, more than half of the from area.

Let’s move on to the code:

Execute this code. Two 2m objects and one 300K object will be added to the heap, and array3 will be set to null.

Byte [] array4 = new byte[2*_1MB];

Then there won’t be enough space in Eden. This is when the second Young GC is triggered.

Let’s continue with the log for the second Young GC:

ParNew: 7223K->0K(9216K)

What does that mean?

Before GC, a total of 7223K objects were used, including 3 2m objects +300K objects in Eden area and 300K objects + unknown objects in FROM area. After GC, the whole new generation is free.

In theory, array2 still references 300K objects after GC. So, you can be sure that this 300K object is not going to be recycled.

But now GC logs clearly tell us that the space usage of the new generation after GC is 0.

Why is that?

Take your time, let’s continue to look at the old space log:

concurrent mark-sweep generation total 10240K, used 601K

You see, 601K was used in the old age. In fact, 601K is the space between 300K objects and unknown objects.

Why are they in the old days?

Because it triggers dynamic age judgments.

If you think about it, first of all, 300K is not a big object. (- XX: PretenureSizeThreshold = 10 m)

Also did not reach the age of 15, because only young GC twice. (- XX: MaxTenuringThreshold = 15)

And survivor is now 1024K, which is large enough to hold 601K of surviving objects. So, none of this is what causes objects to age.

Now because of two Young GCS, the 600K object is alive and takes up more than half the size of the survivor zone. Thus triggering dynamic spatial judgment, into the old age.

At this point, I believe you should be able to understand. If you still don’t understand, welcome to discuss with turnip wechat.

The paper comes from zhongjue shallow, the remaining two situations:

  • After minor GC, survivor space cannot hold all surviving objects
  • The age threshold of a viable object has been reached. Procedure Such as 15

The code for both cases, you can try it out for yourself.

Leave a question for everyone to think about, welcome everyone to leave a message exchange:

When survivor space is insufficient to hold surviving objects, do all objects fall into the old age? Or will some go into the old age and the rest stay in survivor zones?

As for me, I’ve already written the code. You can get it in the background.

JVM code. You can view the actual code for all four cases.

omg

Originally this involves the code actual combat technology to share, should record a video to speak better.

But it’s really hard. Kohlrabi thinks he’s a cheeky guy, but he taped for three hours last night and didn’t finish.

Either stutter halfway, or be nervous halfway, in short, harm, writing an article and recording a video, really two different things. Writing articles, I think more clearly. But recording a video really sucks…

I couldn’t resist making fun of my own voice, and after recording it, I listened to my own voice.

Why… Oh, my… What the hell. Hot ears

Sure enough, it takes courage to listen to your own voice…

However, I will continue to try to record videos, and when I am done, I will share them with you.