JVM optimization experience summary

Mondo Education Updated on 2024-02-08

Through examples and corresponding output explanations, you will have a preliminary understanding of JVM optimization.

The j**a virtual machine has its own complete hardware architecture, such as processors, stacks, registers, etc., and also has a corresponding instruction system. The JVM masks information about the specific operating system platform, so that JA programs can run on a variety of platforms without modification by simply generating the target bytecode that runs on the JVM. When the j**a virtual machine executes bytecode, it actually ends up interpreting the bytecode as the execution of machine instructions on a specific platform.

Note: This article only focuses on JDK7 and Hotspot J**A VMs, and does not focus on the new JVM features introduced in JDK8 and other J**A VMs.

Let's start this article with an example. Suppose you are an ordinary j**a object, you were born in the Eden district, and there are many little brothers and sisters who are similar to you in the Eden district, and you can use the Eden district as a kindergarten, and everyone has been playing in this kindergarten for a long time. The Eden district can't be kept in it endlessly, so when you're a little older, you're going to be sent to school, which is called the survivor zone from elementary school to high school.

At the beginning, you divided the from area in the survivor area, and when you reached the senior year, you entered the to area of the survivor area, and due to the instability of academic performance in the middle, you often tossed back and forth. By the time you're 18 years old, you've graduated from high school, and it's time to break out in society. So you go to the old generation, and there are a lot of people in the old generation. In the Elder Generation, you live for 20 years (each GC plus a year old), and finally you die, and the GC ** doesn't mention it at all, you meet a classmate in the Elder Generation, his name is Edward (the handsome vampire in the City of Light), he and his family will never die, then they live in the Eternal Era.

In the previous article "Introduction to the Working Principle and Use Cases of JVM Junkter", we have introduced the young, old, and immortal generations, and this article mainly talks about how to use these areas to provide better help for system performance. Without repeating these concepts in this article, let's go straight to the point. As we all know, since the cost of full GC is much higher than that of minor GC, there are situations where it is necessary to assign objects to the younger generation as much as possible, which is a wise choice in many cases. Although in most cases, the JVM will try to allocate objects in the eden zone, due to space constraints and other issues, it is likely that some of the younger objects will have to be compressed to the older generation in advance. Therefore, when tuning the JVM parameters, a reasonable space for the younger generation can be allocated to the application to avoid the occurrence of new objects directly entering the older generation to the greatest extent. As shown in Listing 1**, try allocating 4MB of memory space and observe its memory usage.

Listing 1Same-size memory allocation.

public class putineden }
Run Listing 1 with the jvm parameter -xx:+printgcdetails -xmx20m -xms20m and the output is shown in Listing 2.

Listing 2Listing 1 runs the output.

[gc [defnew: 5504k->640k(6144k), 0.0114236 secs] 5504k->5352k(19840k),0.0114595 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 6144k->640k(6144k), 0.0131261 secs] 10856k->10782k(19840k),0.0131612 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 6144k->6144k(6144k), 0.0000170 secs][tenured: 10142k->13695k(13696k),0.1069249 secs] 16286k->15966k(19840k), perm : 376k->376k(12288k)],0.1070058 secs] [times: user=0.03 sys=0.00, real=0.11 secs][full gc [tenured: 13695k->13695k(13696k), 0.0302067 secs] 19839k->19595k(19840k),[perm : 376k->376k(12288k)],0.0302635 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0311986 secs] 19839k->19839k(19840k),[perm : 376k->376k(12288k)],0.0312515 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0358821 secs] 19839k->19825k(19840k),[perm : 376k->371k(12288k)],0.0359315 secs] [times: user=0.05 sys=0.00, real=0.05 secs][full gc [tenured: 13695k->13695k(13696k), 0.0283080 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0283723 secs] [times: user=0.02 sys=0.00, real=0.01 secs][full gc [tenured: 13695k->13695k(13696k), 0.0284469 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0284990 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0283005 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0283475 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0287757 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0288294 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0288219 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0288709 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0293071 secs] 19839k->19839k(19840k),[perm : 371k->371k(12288k)],0.0293607 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 13695k->13695k(13696k), 0.0356141 secs] 19839k->19838k(19840k),[perm : 371k->371k(12288k)],0.0356654 secs] [times: user=0.01 sys=0.00, real=0.03 secs]heapdef new generation total 6144k, used 6143k [0x35c10000, 0x362b0000, 0x362b0000)eden space 5504k, 100% used [0x35c10000, 0x36170000, 0x36170000)from space 640k, 99% used [0x36170000, 0x3620fc80, 0x36210000)to space 640k, 0% used [0x36210000, 0x36210000, 0x362b0000)tenured generation total 13696k, used 13695k [0x362b0000, 0x37010000, 0x37010000)the space 13696k, 99% used [0x362b0000, 0x3700fff8, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706cd20, 0x3706ce00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
The log output shown in Listing 2 shows that the younger generation of Eden is about 5MB in size. Allocate enough young generation space to run Listing 1 with the jvm parameter -xx:+printgcDetails -xmx20m -xms20m-xmn6m and the output is shown in Listing 3.

Listing 3Manifest 1 runs the output after increasing the eden size.

[gc [defnew: 4992k->576k(5568k), 0.0116036 secs] 4992k->4829k(19904k),0.0116439 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 5568k->576k(5568k), 0.0130929 secs] 9821k->9653k(19904k),0.0131336 secs] [times: user=0.02 sys=0.00, real=0.02 secs][gc [defnew: 5568k->575k(5568k), 0.0154148 secs] 14645k->14500k(19904k),0.0154531 secs] [times: user=0.00 sys=0.01, real=0.01 secs][gc [defnew: 5567k->5567k(5568k), 0.0000197 secs][tenured: 13924k->14335k(14336k),0.0330724 secs] 19492k->19265k(19904k), perm : 376k->376k(12288k)],0.0331624 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0292459 secs] 19903k->19902k(19904k),[perm : 376k->376k(12288k)],0.0293000 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0278675 secs] 19903k->19903k(19904k),[perm : 376k->376k(12288k)],0.0279215 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0348408 secs] 19903k->19889k(19904k),[perm : 376k->371k(12288k)],0.0348945 secs] [times: user=0.05 sys=0.00, real=0.05 secs][full gc [tenured: 14335k->14335k(14336k), 0.0299813 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0300349 secs] [times: user=0.01 sys=0.00, real=0.02 secs][full gc [tenured: 14335k->14335k(14336k), 0.0298178 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0298688 secs] [times: user=0.03 sys=0.00, real=0.03 secs]exception in thread "main" j**a.lang.outofmemoryerror: j**a heap space[full gc [tenured:14335k->14335k(14336k), 0.0294953 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0295474 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenured: 14335k->14335k(14336k), 0.0287742 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0288239 secs] [times: user=0.03 sys=0.00, real=0.03 secs][full gc [tenuredat gctimetest.main(gctimetest.j**a:16): 14335k->14335k(14336k), 0.0287102 secs] 19903k->19903k(19904k),[perm : 371k->371k(12288k)],0.0287627 secs] [times: user=0.03 sys=0.00, real=0.03 secs]heapdef new generation total 5568k, used 5567k [0x35c10000, 0x36210000, 0x36210000)eden space 4992k, 100% used [0x35c10000, 0x360f0000, 0x360f0000)from space 576k, 99% used [0x36180000, 0x3620ffe8, 0x36210000)to space 576k, 0% used [0x360f0000, 0x360f0000, 0x36180000)tenured generation total 14336k, used 14335k [0x36210000, 0x37010000, 0x37010000)the space 14336k, 99% used [0x36210000, 0x3700ffd8, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706ce28, 0x3706d000, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
By comparing Listing 2 and Listing 3, it can be found that by setting a large young generation to reserve new objects, setting a reasonable survivor area, and providing the utilization rate of the survivor area, the young objects can be stored in the younger generation. In general, the lack of space in the survivor zone, or when it reaches 50% occupancy, will cause the subject to enter the old age (regardless of its age). Listing 4 creates 3 objects, each with a certain amount of memory space.

Listing 4Memory allocations of different sizes.

public class putineden2 }
Run Listing 4 with the parameter -xx:+printgcDetails -xmx1000m -xms500m -xmn100m -xx:survivorratio=8 and the output is shown in Listing 5.

Listing 5Listing 4 runs the output.

heapdef new generation total 92160k, used 11878k [0x0f010000, 0x15410000, 0x15410000)eden space 81920k, 2% used [0x0f010000, 0x0f1a9a20, 0x14010000)from space 10240k, 99% used [0x14a10000, 0x1540fff8, 0x15410000)to space 10240k, 0% used [0x14010000, 0x14010000, 0x14a10000)tenured generation total 409600k, used 86434k [0x15410000, 0x2e410000, 0x4d810000)the space 409600k, 21% used [0x15410000, 0x1a878b18, 0x1a878c00, 0x2e410000)compacting perm gen total 12288k, used 2062k [0x4d810000, 0x4e410000, 0x51810000)the space 12288k, 16% used [0x4d810000, 0x4da13b18, 0x4da13c00, 0x4e410000)no shared spaces configured.
The log output from Listing 5 shows that the younger generation is allocated 8m and the older generation is also allocated 8m. We can try to add the -xx:targetsurvivorratio=90 parameter, which can improve the utilization of the from zone, so that when the from zone is used to 90%, then send the object to the old generation, and run Manifest 4 ** The output is as shown in Listing 6.

Listing 6Manifest 4 output after modifying the running parameters.

heapdef new generation total 9216k, used 9215k [0x35c10000, 0x36610000, 0x36610000)eden space 8192k, 100% used [0x35c10000, 0x36410000, 0x36410000)from space 1024k, 99% used [0x36510000, 0x3660fc50, 0x36610000)to space 1024k, 0% used [0x36410000, 0x36410000, 0x36510000)tenured generation total 10240k, used 10239k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 99% used [0x36610000, 0x3700ff70, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706cd90, 0x3706ce00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
If the survivorratio is set to 2, the B1 object pre-exists in the younger generation. The output is shown in Listing 7.

Listing 7After modifying the running parameters again, the output of Listing 4 is revealed.

heapdef new generation total 7680k, used 7679k [0x35c10000, 0x36610000, 0x36610000)eden space 5120k, 100% used [0x35c10000, 0x36110000, 0x36110000)from space 2560k, 99% used [0x36110000, 0x3638fff0, 0x36390000)to space 2560k, 0% used [0x36390000, 0x36390000, 0x36610000)tenured generation total 10240k, used 10239k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 99% used [0x36610000, 0x3700fff0, 0x37010000, 0x37010000)compacting perm gen total 12288k, used 371k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706ce28, 0x3706d000, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
In most cases, we choose to assign objects to the younger generation. However, this may not be the case for large memory-intensive objects. Because the presence of large objects in the younger generation is likely to disturb the younger generation GC and destroy the original object structure of the younger generation. Because trying to allocate large objects to the younger generation will likely result in a lack of space, the JVM has to move the younger objects in the younger generation to the older generation in order to have enough space to accommodate the large objects. Because large objects take up a lot of space, it may be necessary to move a large number of small young objects into the older generation, which is quite detrimental to the GC.

For these reasons, it is possible to assign large objects directly to the older generation and maintain the integrity of the object structure of the younger generation, which can improve the efficiency of the GC. If a large object is also a short-lived object, it would be a disaster for the GC, assuming that happens frequently. The old generation, which was supposed to be used to store permanent objects, was crammed with short-lived objects, which also meant that the heap space was shuffled, disrupting the basic idea of generational memory.

Therefore, in the software development process, the use of short-lived large objects should be avoided as much as possible. You can use the -xx:petenuresizethreshold parameter to set the threshold for large objects to go directly to the older generation. When the size of the object exceeds this value, it will be allocated directly in the old generation. Parameter -xx: petenuresizethreshold is only valid for serial collectors and young generation parallel collectors, and the parallel ** collector does not recognize this parameter.

Listing 8Create a large object.

public class bigobj2old }
Running with the jvm parameter -xx:+printgcdetails xmx20m xms20mb gives you the log output shown in Listing 9.

Listing 9Listing 8 Run the output.

heapdef new generation total 6144k, used 1378k [0x35c10000, 0x362b0000, 0x362b0000)eden space 5504k, 25% used [0x35c10000, 0x35d689e8, 0x36170000)from space 640k, 0% used [0x36170000, 0x36170000, 0x36210000)to space 640k, 0% used [0x36210000, 0x36210000, 0x362b0000)tenured generation total 13696k, used 0k [0x362b0000, 0x37010000, 0x37010000)the space 13696k, 0% used [0x362b0000, 0x362b0000, 0x362b0200, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706dac8, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
It can be seen that the object is assigned to the younger generation, taking up 25% of the space. If you want to allocate objects larger than 1MB directly to the elderly, set -xx:petenuresizethreshold=1000000, and the output will be shown in Listing 10 after the program runs.

Listing 10Manifest 8 output after modifying the running parameters.

heapdef new generation total 6144k, used 354k [0x35c10000, 0x362b0000, 0x362b0000)eden space 5504k, 6% used [0x35c10000, 0x35c689d8, 0x36170000)from space 640k, 0% used [0x36170000, 0x36170000, 0x36210000)to space 640k, 0% used [0x36210000, 0x36210000, 0x362b0000)tenured generation total 13696k, used 1024k [0x362b0000, 0x37010000, 0x37010000)the space 13696k, 7% used [0x362b0000, 0x363b0010, 0x363b0200, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706dac8, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
In Listing 10, you can see that when the 1MB is full, it enters the senior generation.

Each object in the heap has its own age. In general, young objects are stored in the younger generation, and old objects are stored in the older generation. In order to do this, the virtual machine maintains an age for each object. If the subject is in the Eden zone and is alive after a GC, it is moved to the survivor zone with the subject's age added by 1. Later, if the subject survives each GC experience, an additional 1 is added to the age. When the age of the subject reaches the threshold, it moves into the old generation and becomes an elderly object. The maximum value of this threshold can be set with the parameter -xx:maxtenuringthreshold, the default value is 15.

While the value of -xx:maxtenuringthreshold may be 15 or greater, this does not mean that new objects need to be of this age to enter the older generation. In fact, the age at which an object actually enters the old generation is dynamically calculated by the virtual machine at runtime based on memory usage, and this parameter specifies the maximum value of the threshold age. That is, the actual age of the promoted older generation is equal to the lesser of the dynamically calculated age and -xx:maxtenuringthreshold. Listing 11 shows that several memories are claimed for 3 objects.

Listing 11Request memory.

public class maxtenuringthreshold }
The parameter is set to: -xx:+printgcdetails -xmx20m -xms20m -xmn10m -xx:survivorratio=2

Run Listing 11 and the output is shown in Listing 12.

Listing 12Listing 11 runs the output.

[gc [defnew: 2986k->690k(7680k), 0.0246816 secs] 2986k->2738k(17920k),0.0247226 secs] [times: user=0.00 sys=0.02, real=0.03 secs][gc [defnew: 4786k->690k(7680k), 0.0016073 secs] 6834k->2738k(17920k),0.0016436 secs] [times: user=0.00 sys=0.00, real=0.00 secs]heapdef new generation total 7680k, used 4888k [0x35c10000, 0x36610000, 0x36610000)eden space 5120k, 82% used [0x35c10000, 0x36029a18, 0x36110000)from space 2560k, 26% used [0x36110000, 0x361bc950, 0x36390000)to space 2560k, 0% used [0x36390000, 0x36390000, 0x36610000)tenured generation total 10240k, used 2048k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 20% used [0x36610000, 0x36810010, 0x36810200, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706db50, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
Change the parameter to -xx:+printgcdetails -xmx20m -xms20m -xmn10m -xx:survivorratio=2 -xx:maxtenuringthreshold=1, run Listing 11 **, and the output is as shown in Listing 13.

Listing 13Manifest 11 output after modifying the running parameters.

[gc [defnew: 2986k->690k(7680k), 0.0047778 secs] 2986k->2738k(17920k),0.0048161 secs] [times: user=0.00 sys=0.00, real=0.00 secs][gc [defnew: 4888k->0k(7680k), 0.0016271 secs] 6936k->2738k(17920k),0.0016630 secs] [times: user=0.00 sys=0.00, real=0.00 secs]heapdef new generation total 7680k, used 4198k [0x35c10000, 0x36610000, 0x36610000)eden space 5120k, 82% used [0x35c10000, 0x36029a18, 0x36110000)from space 2560k, 0% used [0x36110000, 0x36110088, 0x36390000)to space 2560k, 0% used [0x36390000, 0x36390000, 0x36610000)tenured generation total 10240k, used 2738k [0x36610000, 0x37010000, 0x37010000)the space 10240k, 26% used [0x36610000, 0x368bc890, 0x368bca00, 0x37010000)compacting perm gen total 12288k, used 374k [0x37010000, 0x37c10000, 0x3b010000)the space 12288k, 3% used [0x37010000, 0x3706db50, 0x3706dc00, 0x37c10000)ro space 10240k, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)rw space 12288k, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)
As shown in Listing 13, the b1 object is still stored in the younger generation after the program is completed when the program is first run. Before the second run, we lowered the age at which the object was promoted to the Elder to 1. That is, all objects that have passed through the GC once can go directly to the old generation. After the program is run, it can be found that the B1 object has been assigned to the older generation. If you want the object to stay in the younger generation for as long as possible, you can set a large threshold.

In general, a stable heap size is good for garbage**. The way to get a stable heap size is to make -xms and -xmx the same size, i.e. the largest heap is the same as the minimum heap (initial heap). If set this way, the heap size is theoretically constant at runtime, and a stable heap space can reduce the number of GCs. As a result, many server-side applications set the maximum and minimum heaps to the same value.

However, an unstable heap is not useless. Stable heap size can reduce the number of GCs, but it also increases the time per GC. Having the heap size in a zone** can speed up a single GC by compressing the heap space so that the GC can cope with a smaller heap when the system does not need to use large memory. With this in mind, the JVM also provides two parameters for compressing and expanding heap space.

The xx:minheapfreeratio parameter is used to set the minimum idle ratio of heap space, and the default value is 40. When the free memory of the heap space is less than this value, the JVM expands the heap space.

The xx:maxheapfreeratio parameter is used to set the maximum idle ratio of heap space, and the default value is 70. When the free memory of the heap space is greater than this value, the heap space is compressed to obtain a smaller heap.

When -xmx and -xms are equal, the -xx:minheapfreeratio and -xx:maxheapfreeratio arguments are invalid.

Listing 14Heap size settings.

import j**a.util.vector;public class heapsize thread.sleep(1);}
Listing 14 shows the effect of testing -xx:minheapfreeratio and -xx:maxheapfreeratio, and when the running parameter is -xx:+printgcdetails -xms10m -xmx40m -xx:minheapfreeratio=40 -xx:maxheapfreeratio=50, the output is shown in listing 15.

Listing 15Manifest 14 output after modifying the run parameters.

[gc [defnew: 2418k->178k(3072k), 0.0034827 secs] 2418k->2226k(9920k),0.0035249 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 2312k->0k(3072k), 0.0028263 secs] 4360k->4274k(9920k),0.0029905 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 2068k->0k(3072k), 0.0024363 secs] 6342k->6322k(9920k),0.0024836 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 2061k->0k(3072k), 0.0017376 secs][tenured: 8370k->8370k(8904k),0.1392692 secs] 8384k->8370k(11976k), perm : 374k->374k(12288k)],0.1411363 secs] [times: user=0.00 sys=0.02, real=0.16 secs][gc [defnew: 5138k->0k(6336k), 0.0038237 secs] 13508k->13490k(20288k),0.0038632 secs] [times: user=0.00 sys=0.00, real=0.03 secs]
Use the parameter -xx:+printgcdetails -xms40m -xmx40m -xx:minheapfreeratio=40 -xx:maxheapfreeratio=50, and the output is shown in Listing 16.

Listing 16Manifest 14 output after modifying the run parameters again.

[gc [defnew: 10678k->178k(12288k), 0.0019448 secs] 10678k->178k(39616k),0.0019851 secs] [times: user=0.00 sys=0.00, real=0.03 secs][gc [defnew: 10751k->178k(12288k), 0.0010295 secs] 10751k->178k(39616k),0.0010697 secs] [times: user=0.00 sys=0.00, real=0.02 secs][gc [defnew: 10493k->178k(12288k), 0.0008301 secs] 10493k->178k(39616k),0.0008672 secs] [times: user=0.00 sys=0.00, real=0.02 secs][gc [defnew: 10467k->178k(12288k), 0.0008522 secs] 10467k->178k(39616k),0.0008905 secs] [times: user=0.00 sys=0.00, real=0.02 secs][gc [defnew: 10450k->178k(12288k), 0.0008964 secs] 10450k->178k(39616k),0.0009339 secs] [times: user=0.00 sys=0.00, real=0.01 secs][gc [defnew: 10439k->178k(12288k), 0.0009876 secs] 10439k->178k(39616k),0.0010279 secs] [times: user=0.00 sys=0.00, real=0.02 secs]
As can be seen from Listing 16, the garbage in the heap space is stable at a fixed range. In a stable heap, the heap size is always the same, and each time GC is used, a 40MB space is coordinated. Therefore, although the number of GCs is reduced, the speed of a single GC is not as good as that of a heap.

A throughput-first approach will minimize the total time it takes for the system to execute garbage, so consider a parallel collector that focuses on system throughput. On high-performance computers, throughput-first optimization can be performed using the parameter:

j**a –xmx3800m –xms3800m –xmn2g –xss128k –xx:+useparallelgc–xx:parallelgc-threads=20 –xx:+useparalleloldgc
XMX3800M XMS3800M: Sets the maximum and initial values of the j**a heap. In general, in order to avoid frequent heap memory and degrading system performance, we set the maximum heap to equal the minimum heap. Assuming that the smallest heap is reduced to half the maximum heap, i.e. 1900M, then the JVM will run in 1900MB of heap space as much as possible, and if so, the probability of GC occurring is higher;

XSS128K: reduces the size of the thread stack, which allows the remaining system memory to support more threads;

XMN2G: Set the size of the younger generation region to 2GB;

xx:+useparallelgc: The younger generation uses the parallel garbage collector. This is a throughput-focused collector that minimizes GC time.

xx:parallelgc-threads: Sets the number of threads to be used for garbage**, normally, it can be set to be equal to the number of CPUs. However, in the case of a large number of CPUs, it is reasonable to set a relatively small value;

xx:+useparalleloldgc: Sets the collector to be used by the older generation.

The CPU accesses the memory through addressing. The addressing width of a 32-bit CPU is 0 0xffffffff, and the calculated size is 4G, which means that the maximum amount of physical memory that can be supported is 4G. However, in practice, we encountered such a problem, the program needs to use 4G memory, and the available physical memory is less than 4G, resulting in the program having to reduce the memory footprint.

To solve such problems, modern CPUs have introduced the Memory Management Unit (Memory Management Unit). The core idea of the MMU is to use a virtual address instead of a physical address, that is, the CPU uses a virtual address for addressing, and the MMU is responsible for mapping the virtual address to a physical address. The introduction of the MMU solves the limitation of physical memory, and for the program, it is like using 4G memory.

Memory paging is a memory management mechanism proposed on the basis of using MMU. It splits the virtual and physical addresses into pages and page frames at a fixed size (4k), and guarantees that the page is the same size as the page frame. This mechanism, in terms of data structure, ensures efficient access to memory and enables the OS to support non-continuous memory allocation. When the program memory is insufficient, you can also transfer the physical memory pages that are not commonly used to other storage devices, such as disks, which is known as virtual memory.

In a Solaris system, the JVM can support the use of large page sizes. Using large memory paging can enhance the CPU's memory addressing capabilities, which can improve the performance of your system.

j**a –xmx2506m –xms2506m –xmn1536m –xss128k –xx:++useparallelgc–xx:parallelgcthreads=20 –xx:+useparalleloldgc –xx:+largepagesizeinbytes=256m
xx:+largepagesizeInbytes: specifies the size of the large page.

Excessive memory paging can cause the JVM to be partitioned beyond normal when calculating the memory footprint of HEAP internal partitions (perm, new, old), and in the worst case, a zone will occupy an extra page size.

In order to reduce the pause of the application spam **, the first consideration is to use a CMS ** that focuses on the pause of the system, and secondly, in order to reduce the number of full GCs, the object should be reserved for the younger generation as much as possible, because the cost of the younger generation of minor GC is much smaller than that of the older generation of full GC.

j**a –xmx3550m –xms3550m –xmn2g –xss128k –xx:parallelgcthreads=20–xx:+useconcmarksweepgc –xx:+useparnewgc –xx:+survivorratio=8 –xx:targetsurvivorratio=90–xx:maxtenuringthreshold=31
xx:parallelgcthreads=20: set 20 threads for garbage**;

xx: +useparnewgc: the younger generation uses the parallelizer;

xx:+useconcmarksweepgc: older generations use CMS collectors to reduce pause;

xx:+survivorratio: Set the ratio of the eden zone to the survivor zone to 8:1. A slightly larger survivor space can increase the likelihood of objects with a shorter lifespan in the younger generation**, and if the survivor is not large enough, some short-lived objects may go directly into the older generation, which is detrimental to the system.

xx:targetsurvivorratio=90: sets the availability rate of the survivor zone. If you set it to 90%, 90% of the survivor space is allowed to be used. The default value is 50%. Therefore, this setting increases the usage rate of the survivor zone. When the number of objects stored exceeds this percentage, the objects are compressed to the older generation. Therefore, this option is more helpful to keep the object in the younger generation.

xx:maxtenuringthreshold: Sets the age at which the younger subject is promoted to the older generation. The default value is 15 times, i.e. if the subject survives 15 minor gc times, it enters the old generation. This is set to 31 so that the object is kept in the younger generation area as much as possible.

Through the study of this article, readers understand how to reserve new objects in the younger generation, how to let large objects enter the old generation, how to set the age of objects into the old generation, stable j**a heap vs turbulent j**a heap, increase throughput to improve system performance, try to use large memory pagination, use non-possessing garbage ** and other topics, through examples and corresponding output explanations to let readers have a preliminary understanding of jvm optimization. As in other articles, no optimization is fixed, and readers need to judge and practice for themselves to find the right path.

Related Pages