This issue reference from: www.zhihu.com/question/43… The solution is personally original

Key Takeaway

  • Java has generational GC enabled by default
  • When generational GC is enabled, the generational age of surviving objects is increased by 1 when a Young GC occurs, or more specifically replication occurs in a Survivor region.
  • When generation age = -xx :MaxTenuringThreshold specifies the age, the object enters the old age
  • There is also a mechanism for dynamic promotion to older ages, which first calculates the target promotion space based on the ratio specified by -xx :TargetSurvivorRatio (default 50, 50%), and then times the size of a survivor zone. Then add the size of the generation object according to the generation age from small to large until it is greater than the target promotion space. After that, all the objects above the generation age will be promoted.
  • For some GC algorithms, it is also possible to allocate directly over old ages, for example, humongous allocations in G1 GC, which is contiguous allocations in old ages when objects are more than half Region size.

Object allocation

Most objects are allocated on the heap by TLAB, and some objects are allocated on the stack or directly on the heap, maybe Eden or old generation. Also, for some GC algorithms, it is possible to allocate directly over old ages, for example, Humongous allocations in G1 GC, which is contiguous allocations in old ages when objects are more than half Region size.

Here, we’re just going to focus on TLAB assignments. For single-threaded applications, each time memory is allocated, the pointer at the end of the memory address of the last allocated object is recorded, and the allocated object retrieves the allocation from this pointer. This mechanism is called bump-the-pointer. For multithreaded applications, memory allocation needs to consider thread safety. The most straightforward idea would be to use global locking, but this would be poor performance. To optimize this performance, consider allocating a thread-local private memory pool per thread, and then using the bump-the-pointer mechanism to allocate memory. This thread-local private memory pool is called TLAB. Only when the TLAB is full and the memory is applied, the TLAB needs to be expanded or a new TLAB needs to be used. This greatly reduces lock usage.

For a more detailed understanding of TLAB, please refer to: Explore jVM-TLAB principles in depth through JFR and Logging

Generational age

Generational age is in the object header and is used for generational GC. There are four bits to record generational age, so the maximum is 2^ 4-1 = 15. Therefore, configure the maximum generation age -xx :MaxTenuringThreshold=n. The value of n cannot be greater than 16 or less than 0. If it’s 0, it goes straight to the old age. If it equals 16 (but can’t be set to 16), it never enters the old age. The default is 15.

In the event of a Young GC, or rather replication in a Survivor region, the generational age of surviving objects is increased by 1. Since the compiler optimizes the code and the system.gc () call does not immediately trigger gc and is Full GC, it may cause the object to age directly, so we can use volatile to help us actually create the object. Avoid compiler optimizations:

static volatile Object consumer; Public static void main(String[] args) throws Exception {Object instance = new Object(); long lastAddr = VM.current().addressOf(instance); for (int i = 0; i < 10000; Long currentAddr = vm.current ().addressof (instance); long currentAddr = vm.current (). if (currentAddr ! = lastAddr) {/ / address changes, print the object structure ClassLayout layout = ClassLayout. ParseInstance (instance); System.out.println(layout.toPrintable()); lastAddr = currentAddr; } for (int j = 0; j < 10000; J++) {// keep creating new objects // because volatile property updates are not optimized by the compiler consumer = new Object(); }}}Copy the code

This can be observed in conjunction with GC logging. For JVM logging configuration, see this article: OpenJDK 11 JVM logging parameters Parsing and Using

First we run the program with this argument -xmx128m -xlog :gc=info.

[0.016s][info][gc] Using G1
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
[2.540s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 24M->1M(128M) 2.600ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           0d 00 00 00 (00001101 00000000 00000000 00000000) (13)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.627s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.273ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           15 00 00 00 (00010101 00000000 00000000 00000000) (21)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.675s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.063ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           1d 00 00 00 (00011101 00000000 00000000 00000000) (29)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.724s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.068ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           25 00 00 00 (00100101 00000000 00000000 00000000) (37)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.772s][info][gc] GC(4) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.212ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           2d 00 00 00 (00101101 00000000 00000000 00000000) (45)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.821s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.202ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           35 00 00 00 (00110101 00000000 00000000 00000000) (53)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.869s][info][gc] GC(6) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.143ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           3d 00 00 00 (00111101 00000000 00000000 00000000) (61)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.917s][info][gc] GC(7) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.313ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           45 00 00 00 (01000101 00000000 00000000 00000000) (69)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[2.969s][info][gc] GC(8) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.473ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           4d 00 00 00 (01001101 00000000 00000000 00000000) (77)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.021s][info][gc] GC(9) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.283ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           55 00 00 00 (01010101 00000000 00000000 00000000) (85)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.072s][info][gc] GC(10) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.648ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           5d 00 00 00 (01011101 00000000 00000000 00000000) (93)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.122s][info][gc] GC(11) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.585ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           65 00 00 00 (01100101 00000000 00000000 00000000) (101)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.173s][info][gc] GC(12) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.130ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           6d 00 00 00 (01101101 00000000 00000000 00000000) (109)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.224s][info][gc] GC(13) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.078ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           75 00 00 00 (01110101 00000000 00000000 00000000) (117)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.273s][info][gc] GC(14) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.135ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           7d 00 00 00 (01111101 00000000 00000000 00000000) (125)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.322s][info][gc] GC(15) Pause Young (Normal) (G1 Evacuation Pause) 75M->1M(128M) 2.467ms
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           7d 00 00 00 (01111101 00000000 00000000 00000000) (125)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 02 00 20 (00000000 00000010 00000000 00100000) (536871424)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[3.404s][info][gc] GC(16) Pause Young (Normal) (G1 Evacuation Pause) 76M->1M(128M) 0.556ms
[3.485s][info][gc] GC(17) Pause Young (Normal) (G1 Evacuation Pause) 76M->1M(128M) 0.303ms
[3.566s][info][gc] GC(18) Pause Young (Normal) (G1 Evacuation Pause) 76M->1M(128M) 0.288ms
[3.647s][info][gc] GC(19) Pause Young (Normal) (G1 Evacuation Pause) 76M->1M(128M) 0.317ms
[3.727s][info][gc] GC(20) Pause Young (Normal) (G1 Evacuation Pause) 76M->1M(128M) 0.286ms
Copy the code

As you can see, by the 15th GC, the object is in the old age and the memory address does not change as the Young GC progresses. For more information on object headers, see OOPs: Java GC Detail 1. For the most comprehensive understanding of the Java object structure, the object pointer

Dynamic promotion

Dynamic promotion first multiplies survivor zone size by the ratio specified by TargetSurvivorRatio to get target promotion space size. Then add the size of the generation object according to the generation age from small to large until it is greater than the target promotion space. After that, all the objects above the generation age will be promoted.

Dynamic modification Tenuring Threshold, that is, promotion of generational age, source code corresponding: SRC/hotspot/share/gc/serial/defNewGeneration CPP

Adjust_adjust_tenuring_threshold () {public adjust_adjust_adjust_adjust_adjust_size (adjust_adjust_tenuring_threshold ()) { Size_t const survivor_capacity = to()->capacity()/HeapWordSize; // calculate desired_survivor_size, By TargetSurvivorRatio size_t const desired_survivor_size = (size_t)((((double)survivor_capacity) * TargetSurvivorRatio) / 100); Tenuring Threshold _tenuring_threshold = age_table()->compute_tenuring_threshold(desired_survivor_size); If (UsePerfData) {GCPolicyCounters* gc_counters = GenCollectedHeap::heap()->counters(); if (UsePerfData) {GCPolicyCounters* gc_counters = GenCollectedHeap::heap()->counters(); gc_counters->tenuring_threshold()->set_value(_tenuring_threshold); gc_counters->desired_survivor_size()->set_value(desired_survivor_size * oopSize); } age_table()->print_age_table(_tenuring_threshold); }Copy the code

Calculate target Tenuring Threshold corresponding source: SRC/hotspot/share/gc/Shared/ageTable CPP

uint AgeTable::compute_tenuring_threshold(size_t desired_survivor_size) { uint result; // If it is always promoted directly or never promoted, the result is returned directly. Don't calculate the if (AlwaysTenure | | NeverTenure) {assert (MaxTenuringThreshold = = 0 | | MaxTenuringThreshold = = markWord: : max_age + 1, "MaxTenuringThreshold should be 0 or markWord::max_age + 1, but is " UINTX_FORMAT, MaxTenuringThreshold); result = MaxTenuringThreshold; } else { size_t total = 0; uint age = 1; assert(sizes[0] == 0, "no objects with age zero should be recorded"); // Each subgeneration object is added from small to large until greater than desired_survivor_size while (age < table_size) {total += sizes[age]; // check if including objects of age 'age' made us pass the desired // size, if so 'age' is the new threshold if (total > desired_survivor_size) break; age++; } result = age < MaxTenuringThreshold? age : MaxTenuringThreshold; }Copy the code

Daily brush, easy to improve skills, get a variety of offers: