1. Overview of the core of the heap
- There is only one heap memory for a JVM instance, and the heap is also the core area of Java memory management
- The Java heap area is created when the JVM starts, and its space size is determined. It is the largest memory space managed by the JVM
- The size of the heap memory can be adjusted
Code test HeapDemo1
/**
* -Xms10m -Xmx10m
*/
public class HeapDemo1 {
public static void main (String[] args) {
System.out.println( "start..." );
try {
Thread.sleep( 1000000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "end..." );
}
}
Copy code
HeapDemo2
/**
* -Xms20m -Xmx20m
*/
public class HeapDemo2 {
public static void main (String[] args) {
System.out.println( "start..." );
try {
Thread.sleep( 1000000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "end..." );
}
}
Copy code
The two pieces of code are exactly the same, only the heap space set at runtime is different. After executing the two programs at the same time, open jvisualvm (you can execute the jvisualvm command directly in the terminal under mac). Double-click HeapDemo1 Process Viewer visual GC (When you first run jvisualvm, you can select Tools -> Plug-ins , plug-in installation-related visual GC)
You can see in the figure that for the HeapDemo1 process, the total size of the marked Eden, Survivor0, Survivor1 and Old Gen areas is 10m
And the heap HeadDemo2 process, the total size of each part is 20m:
- The "Java Virtual Machine Specification" stipulates that the heap can be in a physically discontinuous memory space, but logically it should be regarded as continuous
- All threads share the Java heap, where you can also divide the thread private buffer (Thread Local Allocation Buffer, TLAB)
- The description of Java in the "Java Virtual Machine Specification" is: all object instances and arrays should be allocated on the heap at runtime. (The heap is the run-time data area from which memory for all class instances and arrays is allocated)
- One point should be emphasized here: it should be that "almost" all object instances are allocated memory here-from the perspective of actual use
- Arrays and objects may never be stored on the stack (except for "allocation on stack") , because a reference is stored in the stack frame, and this reference points to the location of the object or array in the heap
- After the method ends, the objects in the heap will not be removed immediately, they will only be removed during garbage collection
- Heap is the key area for GC to perform garbage collection
Code demo:
package com.nasuf.jvm;
public class SimpleHeap {
private int id;
public SimpleHeap ( int id) {
this .id = id;
}
public void show () {
System.out.println( "My ID is " + id);
}
public static void main (String[] args) {
SimpleHeap s1 = new SimpleHeap( 1 );
SimpleHeap s2 = new SimpleHeap( 2 );
int [] arr = new int [ 10 ];
Object[] arr1 = new Object[ 10 ];
}
}
Copy code
1.1 Memory breakdown
Most modern garbage collectors are designed based on generational collection theory, and the heap space is subdivided into:
- Java7 and before, the heap memory is logically divided into three parts: newborn area + senior care area + permanent area
- Young Generation Space (Young/New)
- It is divided into Eden area and Survivor area
- Tenure Generation Space (Old/Tenure)
- Permanent Space (Perm)
- Young Generation Space (Young/New)
- Java8 and later, the heap memory is logically divided into three parts: newborn area + senior care area + meta space
- Young Generation Space (Young/New)
- It is divided into Eden area and Survivor area
- Tenure Generation Space (Old/Tenure)
- Meta Space (Meta)
- Young Generation Space (Young/New)
Convention: Newborn area = New generation = Young generation, Senior care area = Senior area = Old generation, Permanent area = Permanent generation
If the above code is executed with parameters
Heap
PSYoungGen total 6144K, used 942K [ 0x00000007bf980000 , 0x00000007c0000000 , 0x00000007c0000000 )
eden space 5632K, 16 % used [ 0x00000007bf980000 , 0x00000007bfa6bad0 , 0x00000007bff00000 )
from space 512K, 0 % used [ 0x00000007bff80000 , 0x00000007bff80000 , 0x00000007c0000000 )
to space 512K, 0 % used [ 0x00000007bff00000 , 0x00000007bff00000 , 0x00000007bff80000 )
ParOldGen total 13824K, used 0K [ 0x00000007bec00000 , 0x00000007bf980000 , 0x00000007bf980000 )
object space 13824K, 0 % used [ 0x00000007bec00000 , 0x00000007bec00000 , 0x00000007bf980000 )
Metaspace used 2657K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 287 K , capacity 386 K , committed 512 K , reserved 1048576 K
Copy code
If you switch to JDK7, the following information will be output:
Heap
PSYoungGen total 6656K, used 869K [ 0x00000007ff900000 , 0x0000000800000000 , 0x0000000800000000 )
eden space 6144K, 14 % used [ 0x00000007ff900000 , 0x00000007ff9d9418 , 0x00000007fff00000 )
from space 512K, 0 % used [ 0x00000007fff80000 , 0x00000007fff80000 , 0x0000000800000000 )
to space 512K, 0 % used [ 0x00000007fff00000 , 0x00000007fff00000 , 0x00000007fff80000 )
ParOldGen total 13824K, used 0K [ 0x00000007feb80000 , 0x00000007ff900000 , 0x00000007ff900000 )
object space 13824K, 0 % used [ 0x00000007feb80000 , 0x00000007feb80000 , 0x00000007ff900000 )
PSPermGen total 21504K, used 2656K [ 0x00000007f9980000 , 0x00000007fae80000 , 0x00000007feb80000 )
21504K Space Object, 12 is % Used [ 0x00000007f9980000 , 0x00000007f9c182c0 , 0x00000007fae80000 )
copy the code
2. Set the heap memory size and OOM
- The Java heap area is used to store Java object instances, so the size of the heap is already set when the JVM starts, and you can select-Xmxwith-XmsTo set up
- -XmsUsed to represent the starting memory of the heap area (ie young generation + old generation), equivalent to-XX:InitialHeapSize
- -XIs the operating parameter of jvm
- ms: memory start
- -XmxUsed to represent the maximum memory of the heap area (that is, the young generation + the old generation), which is equivalent to-XX:MaxHeapSize
- mx: memory max
- Once the memory size in the heap area exceeds the maximum memory specified by -Xmx, it will throwOutOfMemoryErrorabnormal
- Usually the two parameters -Xms and -Xmx are configured with the same value, the purpose is to be able to calculate the size of the heap area after the Java garbage collection mechanism cleans up the heap area, thereby improving performance
- by default
- Initial memory size: physical computer memory size/64
- Maximum memory size: physical computer memory size/4
- Initial memory size: physical computer memory size
Code demonstration (output initial memory settings):
package com.nasuf.jvm;
public class HeapSpaceInitial {
public static void main (String [] args) {
//return the Java virtual machine heap total memory
Long initialMemory = Runtime.getRuntime () totalMemory The ()/. 1024/1024 ;
//Returns the Java Virtual Machine The maximum amount of heap memory trying to use
long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024 ;
System.out.println( "-Xms: " + initialMemory + "M" );
System.out.println( "-Xmx: " + maxMemory + "M" );
System.out.println( "The system memory size is:" + initialMemory * 64.0/1024 + "G" );
System.out.println( "The system memory size is:" + maxMemory * 4.0/1024 + "G" );
}
}
Copy code
The output is as follows:
-Xms: 245M
-Xmx: 3641M
The system memory size is: 15. 3125G
The system memory size is: 14. A 22265625G
copy the code
package com.nasuf.jvm;
public class HeapSpaceInitial {
public static void main (String [] args) {
//return the Java virtual machine heap total memory
Long initialMemory = Runtime.getRuntime () totalMemory The ()/. 1024/1024 ;
//Returns the Java Virtual Machine The maximum amount of heap memory trying to use
long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024 ;
System.out.println( "-Xms: " + initialMemory + "M" );
System.out.println( "-Xmx: " + maxMemory + "M" );
try {
Thread.sleep( 1000000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Copy code
Manually set parameters:
-Xms: 575M -Xmx: 575M Copy code
View at this time:
$ jps
19459 Launcher
19507 Jps
19460 HeapSpaceInitial
72644
27743
# nasuf @ promote in/Library/Java/JavaVirtualMachines [16:01:45]
$ jstat -gc 19460
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
25600.0 25600.0 0.0 0.0 153600.0 12288.5 409600.0 0.0 4480.0 774.3 384.0 75.9 0 0.000 0 0.000 0.000
Copy code
among them:
- S0C : S0 Capacity
- S1C : S1 Capacity
- S0U : S0 Used
- S1U : S1 Used
- EC : Eden Capacity
- EU : Eden Used
- OC : Old Capacity
- OU : Old Used
Calculation
In addition, use
Heap
PSYoungGen total 179200K, used 9216K [ 0x00000007b3800000 , 0x00000007c0000000 , 0x00000007c0000000 )
eden space 153600K, 6 % used [ 0x00000007b3800000 , 0x00000007b41001a0 , 0x00000007bce00000 )
from space 25600K, 0 % used [ 0x00000007be700000 , 0x00000007be700000 , 0x00000007c0000000 )
to space 25600K, 0 % used [ 0x00000007bce00000 , 0x00000007bce00000 , 0x00000007be700000 )
ParOldGen total 409600K, used 0K [ 0x000000079a800000 , 0x00000007b3800000 , 0x00000007b3800000 )
object space 409600K, 0 % used [ 0x000000079a800000 , 0x000000079a800000 , 0x00000007b3800000 )
Metaspace used 2658K, capacity 4486K, committed 4864K, reserved 1056768K
class Space Used 287 K , Capacity 386 K , committed 512 K , Reserved 1048576 K
duplicated code
Note: Among them
OutOfMemoryError test
package com.nasuf.jvm;
import java.util.ArrayList;
import java.util.Random;
public class OOMTest {
public static void main (String[] args) {
ArrayList<Picture> list = new ArrayList<>();
while ( true ) {
try {
Thread.sleep( 20 );
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add( new Picture( new Random().nextInt( 1024 * 1024 )));
}
}
}
class Picture {
private byte [] pixels;
public Picture ( int length) {
this .pixels = new byte [length];
}
}
Copy code
Operating parameters:
can be seen
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.nasuf.jvm.Picture.<init>(OOMTest.java: 23 )
com.nasuf.jvm.OOMTest.main AT (OOMTest.java: 15 )
Copy the code
From the sampler, we can further view the object situation in memory:
3. Young generation and old generation
-
Java objects stored in the JVM can be divided into two categories:
- One type is instantaneous objects with a short life cycle, and the creation and demise of such objects are very rapid
- The life cycle of a class of objects is very long, and in some extreme cases it can be consistent with the life cycle of the JVM
-
If the Java heap area is further subdivided, it can be divided into young generation (YoungGen) and old generation (OleGen)
-
Among them, the young generation can be divided into Eden space, Survivor0 space and Survivor1 space (sometimes called from area, to area)
Note: The following parameters are generally not called during development:
-
Configure the proportion of the young generation and the old generation in the heap structure
- default-XX:NewRatio=2, Which means that the new generation accounted for1, The old age accounts for2, The new generation occupies the entire heap space1/3
- Can be modified-XX:NewRatio=4, Which means that the new generation accounted for1, The old age accounts for4, The new generation accounts for the entire heap1/5
- default
-
In HotSpot, the default ratio of Eden space and the other two Survivor spaces is
8:1:1 -
Of course, developers can pass the option
-XX:SurvivorRatioTo adjust this space ratio. such as-XX:SurvivorRatio=8 -
Almost all (not necessarily) Java objects are new in the Eden area (if an object is too large, it may directly enter the old age)
-
Most of the destruction of Java objects takes place in the new generation
- IBM's special research shows that 80% of the objects in the new generation are living and dying.
-
Available options
-XmnSet the maximum memory size of the young generation- This parameter generally uses the default value; if set at the same time-XX:NewRatio, Then it will be-XmnPrevail
- This parameter generally uses the default value; if set at the same time
Code demo
package com.nasuf.jvm;
/**
* -Xms600m -Xmx600m
*/
public class EdenSurvivorTest {
public static void main (String[] args) {
System.out.println( "testing..." );
try {
Thread.sleep( 1000000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Copy code
Execute the above code, the parameters are
By default, the proportion of young generation and old generation is
Or you can pass
$ jps
13490 Launcher
13491 EdenSurvivorTest
12420
13765 Jps
13213 Main
$ jinfo -flag NewRatio 13491
-XX:NewRatio=2
Copy code
From the figure above, it can also be observed that the ratio of Eden:Survivor0:Survivor1 is
4. Graphical object allocation process
4.1 Overview of the object allocation process
Allocating memory for new objects is a very rigorous and complex task. JVM designers not only need to consider how and where to allocate memory, but also because the memory allocation algorithm is closely related to the memory reclamation algorithm, they also need to consider whether memory fragments will be generated in the memory space after the GC has performed the memory reclamation.
- The new object is placed in the Eden area first, this area has a size limit
- When the space in the Eden area is full, the program needs to create objects again, and the garbage collector of the JVM will garbage collect the Eden area (Young GC/Minor GC), destroy the objects in the Eden area that are no longer referenced by other objects, and then load the new objects and put them in the Eden area
- Note: Young GC will not be triggered when the Survivor area is full, but this does not mean that there is no garbage collection in the Survivor area-when the Young GC occurs, the Eden area and the Survivor area will be garbage collected at the same time
- Then move the remaining objects in the Eden area to Survivor0 area
- If you start garbage collection again, the ones that survived last time will be placed in Survivor0 area, if not collected, they will be placed in Survivor1 area
- If you go through garbage collection again, it will be put back into Survivor0 area at this time, and then go to Survivor1 area
- When do you go to the old age? You can set the number of times-XX:MaxTenuringThreshold=<N>, The default is15Times
- In the old age, it is relatively leisurely. When the memory is insufficient in the old age, the GC is triggered again: Major GC, To clean up the memory of the old generation
- If the old generation performs Major GC and finds that the object still cannot be saved, it will generateOOMabnormal
4.2 Graphical process
summary:
- For Survivor0, Survivor1 area: there is exchange after copying, whoever is empty is To
- Regarding garbage collection: Frequently collected in the young generation, rarely collected in the old generation, almost not collected in the permanent generation/meta space
Code test
package com.nasuf.jvm;
import java.util.ArrayList;
import java.util.Random;
/**
* -Xms600m -Xmx600m
*/
public class HeapInstanceTest {
byte [] buffer = new byte [ new Random().nextInt( 1024 * 200 )];
public static void main (String[] args) {
ArrayList<HeapInstanceTest> list = new ArrayList<>();
while ( true ) {
list.add( new HeapInstanceTest());
try {
Thread.sleep( 10 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Copy code
When the program throws an OOM exception:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.nasuf.jvm.HeapInstanceTest.<init>(HeapInstanceTest.java: 10 )
com.nasuf.jvm.HeapInstanceTest.main AT (HeapInstanceTest.java: 15 )
Copy the code
by
5. Minor GC, Major GC and Full GC
When JVM performs GC, it does not always reclaim the above three memory areas (new generation, old generation, method area, or meta space) together. Most of the time, the reclaiming refers to the new generation.
For the implementation of HotSpot VM, the GC in it is divided into two types according to the recovery area: one is partial collection (Partial GC) and the other is full collection (Full GC)
- Partial collection: Garbage collection is not a complete collection of the entire Java heap, which is divided into:
- Young GC (Minor GC/Young GC): Garbage collection only in the young generation (Eden/S0/S1)
- Old age collection (Major GC/Old GC): just garbage collection in the old age
- Currently, only CMS GC will collect the old generation separately
- Note that in many cases Major GC will be confused with Full GC, and it is necessary to distinguish whether it is collection in the old generation or whole heap collection.
- Mixed collection (Mixed GC): Collect garbage collection of the entire young generation and part of the old generation
- Currently, only G1 GC will have this behavior
- Full GC: Collect garbage collection of the entire Java heap and method area
5.1 The Young Generation Minor GC Trigger Mechanism
- When the young generation space is insufficient, the Minor GC will be triggered. The young generation full here refers to the Eden area being full, and the Survivor area full will not trigger the GC (Minor GC will clean up the memory of the young generation each time)
- Because most Java objects have the characteristics of life and death, Minor GC is very frequent, and generally the recovery speed is relatively fast. This definition is clear and easy to understand
- Minor GC will triggerSTM (Stop The World), Suspend the threads of other users, wait until the garbage collection is over, the user threads will resume running
5.2 Major GC/Full GC trigger mechanism in the old age
- Refers to the GC that occurred in the old age. When the object disappears from the old age, we say that Major GC or Full GC has occurred
- The emergence of Major GC is often accompanied by at least one Minor GC (but not absolute, there is a direct Major GC strategy selection process in the collection strategy of the Parallel Scavenge collector)
- That is, when the space in the old generation is insufficient, it will first try to trigger the Minor GC, if the space is not enough later, the Major GC will be triggered
- The speed of Major GC is generally slower than Minor GC10Times more, STW time is longer
- If the memory is not enough after Major GC, throw OOM
5.3 Full GC trigger mechanism
There are five situations that trigger the execution of Full GC:
- transferSystem.gc()When, the system recommends the implementation of Full GC, but not necessarily
- Insufficient space in the old age
- Insufficient method area
- The average size of the old generation after passing the Minor GC is greater than the available memory of the old generation
- When copying from Eden area, Survivor0 (From), area to Survivor1 (To) area, the size of the object is larger than the available memory of To Space, then the object is transferred to the old generation, and the available memory of the old generation is less than the size of the object
Note: Full GC is to be avoided as much as possible during development or tuning, so the pause time will be shorter
5.4 GC log analysis
Code test
package com.nasuf.jvm;
import java.util.ArrayList;
import java.util.List;
/**
* Test Minor GC/Major GC/Full GC
* -Xms9m -Xmx9m -XX:+PrintGCDetails
*/
public class GCTest {
public static void main (String[] args) {
int i = 0 ;
try {
List<String> list = new ArrayList<>();
String a = "nasuf" ;
while ( true ) {
list.add(a);
a = a + a;
i++;
}
} catch (Throwable t) {
t.printStackTrace();
System.out.println( "The number of traversals is:" + i);
}
}
}
Copy code
Log output:
[GC (Allocation Failure) [PSYoungGen: 2013K->496K(2560K)] 2013K->1055K(9728K), 0.0008150 secs] [Times: user= 0.00 sys= 0.00 , real= 0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2456K->496K(2560K)] 3015K->2023K(9728K), 0.0007776 secs] [Times: user= 0.01 sys= 0.00 , real= 0.00 secs]
[ Full GC (Ergonomics) [PSYoungGen: 2456K->0 K (2560K) ] [ParOldGen: 6647K->5472 K (7168K) ] 9103K->5472 K (9728K) , [Metaspace: 2652K->2652 K (1056768K) ], 0.0024768 secs] [Times: user = 0.00 sys= 0.00 , real= 0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 5472K->5472K(9728K), 0.0003580 secs] [Times: user= 0.01 sys= 0.00 , real= 0.00 secs]
[ Full GC (Allocation Failure) [PSYoungGen: 0K->0 K (2560K) ] [ParOldGen: 5472K->5447 K (7168K) ] 5472K->5447 K (9728K) , [Metaspace: 2652K->2652 K (1056768K) ) ], 0.0039904 secs] [Times: user = 0.00 sys= 0.00 , real= 0.00 secs]
The number of traversal is: 17
Heap
PSYoungGen total 2560K, used 81K [ 0x00000007bfd00000 , 0x00000007c0000000 , 0x00000007c0000000 )
eden space 2048K, 4 % used [ 0x00000007bfd00000 , 0x00000007bfd147b8 , 0x00000007bff00000 )
from space 512K, 0 % used [ 0x00000007bff00000 , 0x00000007bff00000 , 0x00000007bff80000 )
to space 512K, 0 % used [ 0x00000007bff80000 , 0x00000007bff80000 , 0x00000007c0000000 )
ParOldGen total 7168K, used 5447K [ 0x00000007bf600000 , 0x00000007bfd00000 , 0x00000007bfd00000 )
object space 7168K, 75 % used [ 0x00000007bf600000 , 0x00000007bfb51ce8 , 0x00000007bfd00000 )
Metaspace used 2684K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 290 K , capacity 386 K , committed 512 K , reserved 1048576 K
java . lang . OutOfMemoryError : Java heap space
at java . util . Arrays . copyOfRange ( Arrays . java :3664)
at java . lang . String .< init > ( String . the Java:207)
at java . Lang . StringBuilder . ToString ( StringBuilder . Java :407)
at com . Nasuf . Jvm . GCTest . Main ( GCTest . Java :18)
Process Finished with Exit code 0
Copy the code
can be seen:
- A Full GC must be executed before the OOM exception is thrown
- [GC (Allocation Failure) [PSYoungGen: 2013K->496K(2560K)] 2013K->1055K(9728K), 0.0008150 secs]in,2013KRefers to the size occupied by the new generation before the GC is executed,496KRefers to the size occupied by the new generation after performing GC,2560KRefers to the total size of the new generation; the latter2013KRefers to the size of the heap space occupied before the GC (here is the same as the size occupied by the young generation before the GC is executed, because when the GC is executed for the first time, the old generation does not have any data, the size of the heap space occupied at this time is Size occupied by the Cenozoic), and1055KIs the size of the heap space occupied after the GC is executed,9728KIs the size of the entire heap space (that is, the set operating parameters-Xms9m -Xmx9m)
6. Heap space generation idea
Why do we need to divide the Java heap into generations? Doesn't it work properly regardless of generations?
- After research, different objects have different life cycles, 70%-99% of objects are temporary objects
- Cenozoic: Eden, two Survivors (also known as from/to, S0/S1) of the same size, and to is always empty
- Old generation: store objects that survived many GCs in the new generation
- In fact, it is completely possible to not divide the generation, the only reason for generation is to optimize the GC performance. If there is no generation, then all the objects are in one place, it is like shutting all the people in a school in a classroom. During the GC, it is necessary to find out which objects are useless, so that all areas of the heap will be scanned. Many objects live and die. If you are generating generations, put the newly created object in a certain place. When GC, first reclaim the object area that is stored for life and death. This will free up a lot of objects. Big room out
7. Memory allocation strategy
If the object was born in Eden and survived the first Minor GC, and can be accommodated by Survivor, it will be moved to the Survivor space, and the age of the object will be set to
The age threshold for the object to be promoted to the old age can be selected through the option
The principles of object allocation for different age groups are as follows :
-
Priority allocation to the Eden area
-
Large objects are directly assigned to the old generation
- Try to avoid too many large objects in the program
Code test
package com.nasuf.jvm; /** * -Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 * -XX:+PrintGCDetails * => Eden: 16m, Survivor0/Survivor1: 2m, Old: 40m */ public class YoungOldSpaceTest { public static void main (String[] args) { byte [] buffer = new byte [ 1024 * 1024 * 20 ]; //20m } } Copy codeLog output:
Heap PSYoungGen total 18432K, used 2650K [ 0x00000007bec00000 , 0x00000007c0000000 , 0x00000007c0000000 ) eden space 16384K, 16 % used [ 0x00000007bec00000 , 0x00000007bee969d0 , 0x00000007bfc00000 ) from space 2048K, 0 % used [ 0x00000007bfe00000 , 0x00000007bfe00000 , 0x00000007c0000000 ) to space 2048K, 0 % used [ 0x00000007bfc00000 , 0x00000007bfc00000 , 0x00000007bfe00000 ) ParOldGen total 40960K, used 20480K [ 0x00000007bc400000 , 0x00000007bec00000 , 0x00000007bec00000 ) object space 40960K, 50 % used [ 0x00000007bc400000 , 0x00000007bd800010 , 0x00000007bec00000 ) Metaspace used 2952K, capacity 4556K, committed 4864K, reserved 1056768K class Space Used 315 K , Capacity 392 K , committed 512 K , Reserved 1048576 K duplicated codecan be seen
ParOldGen total 40960K, used 20480K, 20m objects are directly allocated to the old generation -
Long-lived objects are allocated to the old generation
-
Dynamic object age judgment
- If the total size of all objects of the same age in the Survivor area is greater than half of the Survivor space, objects with an age greater than or equal to this age can directly enter the old age without waitingMaxTenuringThresholdAge required in
- If the total size of all objects of the same age in the Survivor area is greater than half of the Survivor space, objects with an age greater than or equal to this age can directly enter the old age without waiting
-
Space allocation guarantee
- -XX:HandlePromotionFailure
8. TLAB
Why is there TLAB (Thread Local Allocation Buffer)
- The heap area is a thread shared area, any thread can access the shared data in the heap area
- Since the creation of object instances is very frequent in the JVM, it is not thread-safe to divide the memory space from the heap area in a concurrent environment
- In order to avoid multiple threads operating the same address, it is necessary to use mechanisms such as locking, which in turn affects the allocation speed
What is TLAB?
- From the perspective of memory model rather than garbage collection, continue to divide the Eden area. JVM allocates a private cache area for each thread, which is contained in the Eden space
- When multiple threads allocate memory at the same time, the use of TLAB can avoid a series of non-thread safety issues, and at the same time can improve the throughput of memory allocation, so we can call this memory allocation method a fast allocation strategy
- All known JVMs derived from OpenJDK provide the design of TLAB
TLAB's further explanation:
-
Although not all object instances can successfully allocate memory in TLAB, JVM is indeed the first choice for TLAB memory allocation
-
In the program, developers can select
-XX:UseTLABSet whether to open TLAB spaceCode verification TLAB is on:
public class TLABTest { public static void main (String[] args) { System.out.println( "testing..." ); try { Thread.sleep( 1000000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } Copy codeAfter the program is started, execute it on the command line:
$ jps 25059 27944 Launcher 27945 TLABTest 27947 Jps 13213 Main $ jinfo -flag UseTLAB 27945 -XX:+UseTLAB Copy codeOutput
-XX:+UseTLABIndicates that TLAB is turned on by default -
By default, TLAB memory space is very small, only takes up the entire space of Eden
1%, Of course we can pass the option-XX:TLABWasteTargetPercentSet the percentage of Eden space occupied by TLAB space -
Once the object fails to allocate memory in the TLAB space , the JVM will try to ensure the atomicity of data operations by using the locking mechanism , thereby directly allocating the memory in the Eden space
The whole process of object allocation:
9. Summary of parameter settings for heap space
Official website description https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
- -XX:+PrintFlagsInitialView the default initial values of all parameters
- -XX:+PrintFlagsFinalView the final value of all parameters (there may be modifications, not the initial value)
- -XmsInitial heap space memory (default is the physical memory1/64)
- -XmxMaximum heap space memory (default is physical memory1/4)
- -XmnSet the size of the young generation (initial value and maximum value)
- -XX:NewRatioConfigure the proportion of the young generation and the old generation in the heap structure
- -XX:SurvivorRatioSet the ratio of Eden and S0/S1 space in the new generation
- -XX:MaxTenuringThresholdSet the maximum age of Cenozoic garbage
- -XX:+PrintGCDetailsOutput detailed GC processing log
- Print brief GC information:
- -XX:+PrintGC
- -verbose:gc
- Print brief GC information:
- -XX:HandlePromotionFailureWhether to set up a space allocation guarantee. Before the occurrence of Minor GC, the virtual machine checks whether the maximum available contiguous space in the old generation is greater than the total space of all objects in the new generation
- If it is greater than, then the Minor GC is safe
- If it is less, then the virtual machine view-XX:HandlePromotionFailureWhether the setting value allows guarantee failure
- in caseHandlePromotionFailure=true, Then it will continue to check whether the maximum available continuous space in the old age is greater than the average size of the objects promoted to the old age
- If it is greater than, try a Minor GC, but this time Minor GC is still risky
- If it is less than, then perform a Full GC instead
- in caseHandlePromotionFailure=false, Then perform a Full GC instead
- in case
HandlePromotionFailureThe parameters will no longer affect the space allocation guarantee strategy of the virtual machine. Observe the source code changes in OpenJDK, although the source code is also definedHandlePromotionFailureParameter, but it is no longer used in the code. The rule after JDK6 Update24 is that as long as the continuous space of the old generation is greater than the total size of the new generation object or the average size of previous promotions, Minor GC will be performed, otherwise Full GC will be performed
10. Escape analysis
There is a description of Java heap memory in "In-depth Understanding of the Java Virtual Machine":
With the development of JIT compilers and the gradual maturity of escape analysis techniques , on- stack allocation and scalar replacement optimization techniques will cause some subtle changes. All objects are allocated to the heap and gradually become less "absolute".
In the Java virtual machine, objects are allocated memory in the Java heap, which is a common sense. However, there is a special case, that is, if it is found after Escape Analysis that an object has no escape method, it may be optimized to be allocated on the stack . In this way, there is no need to allocate memory on the heap, and there is no need for garbage collection. This is also the most common off-heap storage technology
In addition, the aforementioned TaoBaoVM based on OpenJDK deep customization, in which the innovative GCID (GC Invisible Heap) technology implements off-heap, moves Java objects with a long life cycle from the heap to outside of the heap, and GC cannot manage the internal GCIH Java objects in order to achieve the purpose of reducing the frequency of GC recycling and improving the efficiency of GC recycling
10.1 Overview
- How to allocate objects on the heap to the stack requires the use of escape analysis methods
- This is a cross-function global data flow analysis algorithm that can effectively reduce the synchronization load and memory heap allocation pressure in Java programs
- Through escape analysis, the Java HotSpot compiler can analyze the scope of use of a new object reference, so as to decide whether to allocate this object to the heap
- The basic behavior of escape analysis is the dynamic scope of the analysis object
- When an object is defined in a method, the object is only used in the method memory, it is considered that no escape has occurred
- When an object is defined in a method, it is referenced by an external method, it is considered to have escaped. For example, as a call parameter passed to other places
Code example
public void test () {
Object o = new Object();
//use v
//...
o = null ;
}
Copy code
Objects that have not escaped can be allocated on the stack. As the method execution ends, the stack space is removed
public static StringBuffer createStringBuffer (String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb;
}
Copy code
If the above code wants StringBuffer sb not to escape the method, you can write it like this:
public static String createStringBuffer (String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}
Copy code
More escape analysis code examples
package com.nasuf.jvm;
/**
* Escape analysis
*
* How to quickly determine whether an escape has occurred depends on whether the object entity of new may be called outside the method
*/
public class EscapeAnalysis {
public EscapeAnalysis obj;
/**
* The method returns the EscapeAnalysis object, and escape occurs
* @return
*/
public EscapeAnalysis getInstance () {
return obj == null ? new EscapeAnalysis(): obj;
}
/**
* Assign values to member attributes and escape occurs
*/
public void setObj () {
this .obj = new EscapeAnalysis();
}
/**
* Object scope is only valid in the current method, no escape occurs
*/
public void useEscapeAnalysis () {
EscapeAnalysis e = new EscapeAnalysis();
}
/**
* Quoting the value of a member variable, escape occurs
* Note: This example focuses on the object entity of new in the getInstance() method, not the variable e
*/
public void useEscapeAnalysis2 () {
EscapeAnalysis e = getInstance();
//getInstance().xxx() will also escape
}
}
Copy code
parameter settings:
- After the JDK 6u23 version, the escape analysis has been enabled by default in HotSpot
- If you are using an earlier version, developers can pass:
- Options-XX:+DoEscapeAnalysisExplicitly enable escape analysis
- Pass option-XX:+PrintEscapeAnalysisView the screening results of escape analysis
- Options
Conclusion: If local variables can be used in development, don t define them outside the method
10.2 Code optimization
Using escape analysis, the compiler can optimize the code as follows:
- Allocated on the stack. Convert heap allocation into stack allocation. If an object is allocated in a subroutine, so that the pointer to the object will never escape, the object may be a candidate for stack allocation instead of heap allocation
- Synchronization is omitted. If an object is found to be accessed only from one thread, then the operation of this object can be ignored for synchronization
- Separate objects or scalar replacements. Some objects may not need to exist as a continuous memory structure to be accessed, so part (or all) of the object may not be stored in the memory, but stored in the CPU register
10.2.1 Allocation on the stack
- According to the results of escape analysis during compilation, the JIT compiler found that if an object has no escape method, it may be optimized for allocation on the stack. After the allocation is completed, execution continues in the call stack, and finally the thread ends, the stack space is reclaimed, and the local variable object is also reclaimed. This eliminates the need for garbage collection
- Common escape scenarios
- Assign values to member variables, method return values, and pass by instance references
Code demo
package com.nasuf.jvm;
/**
* -XX:+PrintGCDetails
*/
public class StackAllocation {
public static void main (String[] args) {
long start = System.currentTimeMillis();
for ( int i = 0 ; i < 10000000 ; i++) {
alloc();
}
//View execution time
long end = System.currentTimeMillis();
System.out.println( "The time spent is:" + (end-start) + "ms" );
//In order to check the number of objects in the heap memory, thread sleep
try {
Thread.sleep( 1000000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void alloc () {
//No escape occurred
User user = new User();
}
static class User {}
}
Copy code
At this time, the operating parameter is 1G of memory space (large enough), and the escape analysis is turned off
Time spent: 86 msCopy
code
use
You can see that there are 10 million User objects in memory
If the operating parameters are modified to
The time taken is: 4 msCopy
code
Modify the operating parameters again to reduce the initial and maximum memory space to
[GC (Allocation Failure) [PSYoungGen: 65536K->528K(76288K)] 65536K->536K(251392K), 0.0007652 secs] [Times: user= 0.00 sys= 0.00 , real= 0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 66064K->480K(76288K)] 66072K->488K(251392K), 0.0006022 secs] [Times: user= 0.00 sys= 0.00 , real= 0.00 secs]
Time spent: 55 msCopy
code
You can see that the program memory has executed GC
And if you turn on the escape analysis
The time taken is: 5 msCopy
code
10.2.2 Synchronous omission
- The cost of thread synchronization is quite high, the consequence of synchronization is to reduce concurrency and performance
- When dynamically compiling the synchronization block, the JIT compiler can use escape analysis to determine whether the lock object used by the synchronization block can only be accessed by one thread and not released to other threads . If not, then the JIT compiler will cancel the synchronization of this part of the code when compiling this synchronization block. This can greatly improve concurrency and performance. This process of canceling synchronization is called synchronization omission, or lock elimination
Such as the following code:
public void f () {
Object hollis = new Object();
synchronized (hollis) {
System.out.println(hollis);
}
}
Copy code
The hollis object is locked in the code, but the life cycle of the hollis object is only in the f() method, and will not be accessed by other threads, so it will be optimized during the JIT compilation stage:
public void f () {
Object hollis = new Object();
System.out.println(hollis);
}
Copy code
But note that in the bytecode file of the f() method, we can still see the bytecode instructions related to the use and exit of the monitor
0 new # 2 <java/lang/Object>
3 dup
4 invokespecial # 1 <java/lang/Object.<init>>
7 astore_1
8 aload_1
9 dup
10 astore_2
11 monitorenter
12 getstatic # 3 <java/lang/System.out >
15 aload_1
16 invokevirtual # 4 <java/io/PrintStream.println>
19 aload_2
20 monitorexit
21 goto 29 (+ 8 )
24 astore_3
25 aload_2
26 the monitorexit
27 aload_3
28 athrow
29 return
duplicated code
10.2.3 Separated objects or scalar replacement
Scalar refers to data that cannot be broken down into smaller data. The primitive data type in Java is a scalar
In contrast, the data that can be decomposed is called Aggregate . The object in Java is the aggregate, because it can be decomposed into other aggregates and scalars.
In the JIT phase, if it is found that an object cannot be accessed by the outside world after escape analysis, then after JIT optimization, the object will be disassembled into several member variables contained therein instead. This process is scalar substitution . Parameters
For example, the following code:
package com.nasuf.jvm;
public class ScalarReplace {
public static class User {
public int id;
public String name;
}
public static void alloc () {
User u = new User(); //No escape occurs, perform scalar replacement
u.id = 5 ;
u.name = "nasuf" ;
}
public static void main (String[] args) {
long start = System.currentTimeMillis();
for ( int i = 0 ; i < 10000000 ; i++) {
alloc();
}
long end = System.currentTimeMillis();
System.out.println( "The time spent is:" + (end-start) + "ms" );
}
}
Copy code
The operating parameters are
[GC (Allocation Failure) 25600K->504K(98304K), 0.0008521 secs]
[GC (Allocation Failure) 26104K->440K(98304K), 0.0006371 secs]
[GC (Allocation Failure) 26040K->472K(98304K), 0.0004948 secs]
[GC (Allocation Failure) 26072K->472K(98304K), 0.0004898 secs]
[GC (Allocation Failure) 26072K->424K(98304K), 0.0004289 secs]
[GC (Allocation Failure) 26024K->456K(101888K), 0.0005304 secs]
[GC (Allocation Failure) 33224K->352K(101888K), 0.0005369 secs]
[GC (Allocation Failure) 33120K->352K(100864K), 0.0003237 secs]
Time spent is: 46 MS
copy the code
You can see that the object memory allocation time is
The time taken is: 4 msCopy
code
It can be seen that the memory is not GC, and the object allocation time is greatly shortened to
public static void alloc () {
User u = new User(); //No escape occurs, perform scalar replacement
u.id = 5 ;
u.name = "nasuf" ;
}
Copy code
Is actually replaced by:
public static void alloc () {
int id = 5 ;
String name = "nasuf" ;
}
Copy code
This eliminates the need for heap memory allocation, reduces GC, and directly allocates on the stack.
But it should be noted that the premise of the escape analysis is to start the JVM
$ java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
10.3
- 1999 JDK1.6
- JVM , , JVM Oracle HotSpot JVM
- JDK7 JDK intern intern