Interview notes: in depth understanding of JVM

01. Preface

Brush Douban saw the third edition of in-depth understanding of JVM, so he bought it to update the JVM knowledge. This article is a note for personal Review only.

02. Java memory area and memory overflow

03. Runtime data area

‍‍

‍‍

Reference: JVM specification, Memories of a Java Runtime

Heap: the memory area created by - Xmx, -Xms when the JVM is started. It is used to allocate the memory required by objects and arrays. It is managed and recycled by GC

Method area: stores the class information loaded by the JVM (fields, bytecode instructions of member methods, etc.), runtime constant pool (literal, symbol reference, etc.), JIT compiled Code Cache, etc; Before JDK8, Hotspot stored the method area in the permanent generation heap memory, and then abandoned the permanent generation with reference to JRockit and stored it in the Metaspace area of local memory

Direct memory: JDK1.4 introduces NIO, uses Native/Unsafe libraries to allocate system memory directly, and uses buffer and channel to interact with it to avoid the overhead of copying between system memory and JVM heap memory

Thread private memory

  • Program counter: records the position of the next instruction to be executed by the current thread, resumes execution after context switching, and is updated by the bytecode interpreter
  • JVM stack
  • Describes the memory model of Java method execution: when executing a new method, create a stack frame and store information such as local variable table and operand stack
  • Storage unit: variable slot, long and double account for 2 slots, and other basic data types and reference types account for 1. Therefore, the total length of the table can be known during compilation
  • Native method stack: executes native C/C + + methods

04. JVM object

1. Create object

Allocate heap memory: after the class is loaded, the memory required by its object is determined; Heap memory is shared by multiple threads. If concurrent objects compete for memory through CAS optimistic locks, the efficiency is low. Therefore, when a thread is created, a private allocation buffer (TLAB: Thread Local Allocation Buffer) is allocated to the heap memory

  • Memory model
  • Allocation process

Note: when the remaining space of TLAB is insufficient to allocate new objects, but it is less than the maximum wasted space threshold, a new TLAB will be created by locking

Zero value initializes the heap memory of the object, sets the object header information, and executes the constructor () V

2. Memory layout of objects

Object header

  • Mark Word: records the runtime information of the object, such as hashCode, GC generation age, and the tail 2 bit s are used to mark the lock status
  • Class Pointer: refers to the class information to which it belongs
  • Array length (optional, the object is an array): 4 bytes store its length

Object data: the values of various fields are stored next to each other by width

Alignment and filling: memory alignment is an integer multiple of 1 word length to reduce CPU bus cycle

Verification: openjdk/jol checks object memory layout

public class User {
private int age = -1;
private String name = "unknown";
}

// java -jar ~/Downloads/jol-cli-latest.jar internals -cp . com.jol.User
OFF  SZ               TYPE DESCRIPTION               VALUE
0   8                    (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
8   4                    (object header: class)    0xf8021e85 // User.class reference address
 12   4                int User.age                  -1         // Basic types store values directly
 16   4   java.lang.String User.name                 (object)   // A reference type that points to a String object in the runtime constant pool
 20   4                    (object alignment gap)               // There is a 4-byte memory fill
Instance size: 24 bytes

05. Memory overflow

Heap memory: - Xms specifies the initial size of the heap. When the memory occupied by a large number of objects that cannot be recycled exceeds the upper limit of - Xmx, a memory overflow OutOfMemoryError will occur

  • Troubleshooting: locate large objects that cannot be recycled and find their GC Root reference path through the *. hprof heap dump file generated by Eclipse MAT analysis - XX:+HeapDumpOnOutOfMemory
  • Solution: in case of memory leakage, modify the code and recycle large objects in time by means of null explicit assignment and virtual reference; In case of memory overflow, all large objects must survive. Increase - Xmx, reduce the life cycle of large objects, and check whether the use of data structure is reasonable
// -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
public class HeapOOM {
 static class OOMObject {}
 public static void main(String[] args) {
  List<OOMObject> vs = new ArrayList<>();
  while (true)
     vs.add(new OOMObject());
 }
}

Analyzing GC Root, it is found that com.ch02.HeapOOM object indirectly references a large number of OOMObject objects, occupying a total of 15.4MB heap memory, which cannot be recycled, resulting in OOM

Stack memory: - Xss specifies the stack size. When the stack depth exceeds the threshold (such as recursive calls that do not trigger termination conditions), the local method variable table is too large, etc., it may lead to memory overflow StackOverflowError

Method area: - XX:MetaspaceSize specifies the initial size of meta space, - XX:MaxMetaspaceSize specifies the maximum size. The default is - 1 unlimited. If a large number of classes are generated dynamically at runtime, OOM may be triggered

Runtime constant pool: strObj.intern() dynamically puts the first string object into the string constant pool and returns it. Before JDK7, it will be copied to the permanent generation, and then it will directly reference the heap object

String s1 = "java"; // When the class is loaded, the symbol is copied from the bytecode constant pool to the runtime constant pool, and the string object initialized in the parsing phase
String s2 = "j";
String s3 = s2 + "ava"; // Dynamically allocated string object on heap
println(s3 == s1);          // false
println(s3.intern() == s1); // true / / already exists in string constant pool

Direct memory: - XX:MaxDirectMemorySize specifies the size. It is as large as - Xmx by default. It is not managed by GC. When the requested memory exceeds the threshold, OOM

06. Garbage collection and memory allocation

GC can be divided into three sub questions: which memory can be recycled, when and how

07. GC conditions

1. reference counting algorithm

Principle: each object maintains a reference counter rc. When it is referenced by assignment and parameter passing, rc + +. When the reference variable is dereferenced by modifying the direction and leaving the function scope, rc --, when it decreases to 0, it indicates that the object can no longer be used and can be recycled. Pseudo code:

assign(var, obj):
incr_ref(obj) # self = self # First increase and then decrease to avoid early memory release caused by referencing itself
decr_ref(var)
var = obj
 
incr(obj):
obj.rc++

decr(obj):
obj.rc--
if obj.rc == 0:
  remove_ref(obj) # Break the reference relationship between obj and other objects
  gc(obj)         # Reclaim obj memory

Advantages: the idea is simple, the object is useless or recycled, and the delay is low. It is suitable for scenes with less memory

Disadvantages: the objects in this algorithm are isolated, and the authenticity and validity of the objects cannot be checked from the global perspective. The objects of both sides of the circular reference need to be detected and recycled by introducing an external mechanism, as shown in the red circle in the figure below (source: what is garbage collection)

2. reachability analysis

Principle: starting from the objects that will not be recycled (GC Roots), search the global object graph outward. The unreachable objects can no longer be used and can be recycled; Common objects that can be used as GC Root are:

  • Execution context: heap objects referenced by parameters, local variables, temporary variables, etc. in the JVM stack
  • Global reference: the object pointed to by the static reference and constant reference of the class in the method area (such as the string object in StringTable)

Advantages: no object is required to maintain GC meta information, and the overhead is small; Single scan can identify and recycle objects in batch, with high throughput

Disadvantages: in the multithreaded environment, the reference relationship between objects is changing at any time. In order to ensure the accuracy of GC Root tag, it needs to be carried out in the unchanged snapshot, resulting in Stop The World (hereinafter referred to as STW) Caton

3. Four reference types

reference type

class

Recovery time

Strong reference

-

As long as there is a reference chain with GC Root, it is not recycled

Soft reference

SoftReference

Only objects referenced by soft references are recycled when memory is still insufficient after GC

Weak reference

WeakReference

Objects referenced only by weak references will be reclaimed regardless of whether there is enough memory

Virtual reference

PhantomReference

The referenced object is not aware, performs normal GC, and only notifies the virtual reference (callback) during recycling

Example: limit heap memory to 50MB, including 30MB for the new generation and 20MB for the old generation; Allocate 10MB byte [] objects five times in sequence, use only soft references for reference, and observe the GC process

public static void main(String[] args) {
// softRefList --> SoftReference --> 10MB byte[] 
List<SoftReference<byte[]>> softRefList = new ArrayList<>();
ReferenceQueue<byte[]> softRefQueue = new ReferenceQueue<>(); // Invalid reference queue
for (int i = 0; i < 5; i++) {
  SoftReference<byte[]> softRef = new SoftReference<>(new byte[10*1024*1024], softRefQueue);
  softRefList.add(softRef);

  for (SoftReference<byte[]> ref : softRefList) // dump all objects pointed to by soft references. Check whether they have been recycled
      System.out.print(ref.get() == null ? "gced " : "ok ");
  System.out.println();
}
Reference<? extends byte[]> ref = softRefQueue.poll();
while (ref != null) {
  softRefList.remove(ref); // Dereference the soft reference object itself
  ref = softRefQueue.poll();
}
System.out.println("effective soft ref: " + softRefList.size()); // 2
}

// java -verbose:gc -XX:NewSize=30m -Xms50m -Xmx50m -XX:+PrintGCDetails com.ch02.DemoRef
ok 
ok ok 
// When allocating the third [] byte, Eden GC is invalid, triggering Full GC to promote a [] byte to the elderly area
// At this time, the three byte s [] are only referenced by soft references and are marked for secondary recycling (if they are weak references, Eden has been recycled at this time)
[GC (Allocation Failure) --[PSYoungGen: 21893K->21893K(27136K)] 21893K->32141K(47616K), 0.0046324 secs]
[Full GC (Ergonomics) [PSYoungGen: 21893K->10527K(27136K)] [ParOldGen: 10248K->10240K(20480K)] 32141K->20767K(47616K), [Metaspace: 2784K->2784K(1056768K)], 0.004 secs]
ok ok ok
// Again, the first three byte s [] are all recycled
[GC (Allocation Failure) --[PSYoungGen: 20767K->20767K(27136K)] 31007K->31007K(47616K), 0.0007963 secs]
[Full GC (Ergonomics) [PSYoungGen: 20767K->20759K(27136K)] [ParOldGen: 10240K->10240K(20480K)] 31007K->30999K(47616K), [Metaspace: 2784K->2784K(1056768K)], 0.003 secs]
[GC (Allocation Failure) --[PSYoungGen: 20759K->20759K(27136K)] 30999K->30999K(47616K), 0.0007111 secs]
[Full GC (Allocation Failure) [PSYoungGen: 20759K->0K(27136K)] [ParOldGen: 10240K->267K(20480K)] 30999K->267K(47616K), [Metaspace: 2784K->2784K(1056768K)], 0.003 secs]
gced gced gced ok
gced gced gced ok ok

4. finalize

Principle: if the object is unreachable and marked recyclable, it will be filtered whether the finalize() has been rewritten and executed. If it passes, the object will be put into the F-Queue queue and wait for the low priority background Finalizer thread to trigger the execution of its finalize() (the end of execution is not guaranteed), Object can establish a reference relationship with any node in the GC Root object graph in finalize to escape GC

Usage: the finalize mechanism is not equivalent to the destructor in C + +, and its execution result is uncertain. It is not recommended and can be replaced by try finally

08. GC algorithm

Generational collection theory

Two generational hypotheses: in line with the actual situation of most programs

Correspondingly, the JVM heap is divided into two different regions, and objects are classified by age, taking into account GC time and memory utilization

Cross generational reference

  • Problem: the old generation will refer to the new generation, and the new generation GC needs to traverse a large number of surviving objects in the old generation to analyze accessibility and high time complexity
  • Background: the objects of mutual reference tend to exist and die at the same time. For example, the new generation in the cross generational reference relationship is bound to be promoted gradually, and finally the cross generational relationship is eliminated
  • Hypothesis: compared with intergenerational references, intergenerational references account for only a few, and there is no need to scan the elderly generation
  • Implementation: the new generation maintains the global data structure: the Remembered Set, which divides the old generation into multiple sub blocks, marks the sub blocks with cross generation references, and waits for subsequent scanning; Cost: to ensure the correctness of the memory set, it is necessary to keep synchronization when cross generation references are established or disconnected

09. Mark sweep

  • Principle: Mark unreachable objects, clean and recycle them uniformly, and vice versa
  • Disadvantages: the execution efficiency is unstable, and the recovery time depends on the number of active objects; If there are too many memory fragments, there will be continuous memory (array) with sufficient memory but unable to allocate too large

10. Mark copy: mark copy

  • Theory: cut the heap memory into two equal parts A and B, use only a each time, mark the living object and copy it to B after it is used up, and execute swap after clearing a
  • Advantages: it directly aims at half area recycling and has no memory fragmentation problem; To allocate memory, you only need to move the heap top pointer for efficient sequential allocation
  • Disadvantages: when there are A large number of live objects in area A, the replication overhead is large; Area B is idle for A long time, resulting in serious memory waste
  • Practice: for the new generation with few surviving objects, it is not necessary To allocate by 1:1, but by 8:1:1 memory layout. Eden and From areas are used at the same time, and only To area will be idle (guarantee mechanism: if To area is not enough To accommodate the surviving objects after Minor GC, it will be promoted To the elderly area)

11. Mark compact

  • Principle: after marking the living objects, move them to one side of the memory space, and then recycle the memory outside the boundary
  • Advantages: simple memory model, no memory fragments, reducing the time cost of memory allocation and access, and improving throughput
  • Disadvantages: STW needs to update the reference relationship synchronously when moving objects, which will increase the delay

12. HotSpot GC algorithm details

13. Initiating GC: safe points and safe areas

Sketch Map

14. Accelerating GC: CardTable

Question: there will be cross generational references to the collection area (Cenozoic) in the non collection area (old age). How to avoid full scanning of the former?

Card table: byte array implementation of memory set; Divide the old generation memory into sub memory blocks with the size of Card Page (512KB). If a cross generation reference is created, the corresponding card will be marked as dirty. During GC, only the sub memory blocks marked as dirty in the old generation need to be scanned

Write barrier: different from volatile memory barrier that disables instruction rearrangement, write barrier in GC is a mechanism to perform additional hook actions when object references are updated. Simple implementation:

void oop_field_store(oop* field, oop new_val) { // oop: ordinary object pointer
// pre_write_barrier(field, new_val); //  Barrier before writing: execute before updating and use oop old state
*field = new_val;
post_write_barrier(field, new_val); // Post write barrier: execute after updating
}

Use the write barrier to ensure the real-time update of CardTable (Figure source: The JVM Write Barrier - Card Marking)

15. Correct GC: concurrent reachability analysis

Reference speech: Shenandoah: the garbage collector that court by Aleksey shipilev

**Problem: * * the object source of GC Roots is fixed, so the STW time during enumeration is short and controllable. However, the time complexity of subsequent reachability analysis is positively correlated with the number of objects in the heap, that is, the more objects in the heap, the more complex the object graph, and the STW time after the heap becomes larger is unacceptable

**Resolution: * * concurrency flag. Lead to new problems: the user thread dynamically establishes and dereferences, and the graph structure changes during the marking process, resulting in unreliable results; Proof: three color method is used to describe the object state

  • White: objects that have not been accessed by the collector; The beginning of the analysis is white, but the end of the analysis is still white
  • Gray: accessed by the collector, but at least one reference on it has not been scanned (intermediate state)
  • Black: accessed by the collector, all references on it have been scanned, and there is a reference chain, which is a living object; If other objects refer to black objects, they do not need to be scanned and must survive; Black cannot directly reference white

STW has no concurrent correct flag: the top 3 objects will be recycled

Concurrent modification of references by user threads will lead to invalid tag results. There are two cases:

  • Less recycling, the object is marked as alive, but the user dereferences it: floating garbage is generated, acceptable, waiting for the next GC
  • Error recycling: the object is marked as recyclable, but the user creates a new reference: the actual living object is recycled, memory error

The paper uniprocessor garbage collection technologies - Paul R. Wilson § 3.2 proves that "the actual living object is marked as recyclable" must meet two conditions (in time order) at the same time

  • Insert one or more new references from black to white
  • Delete all direct and indirect references from gray to white

In order to correctly realize the marking, one of the conditions can be broken (analogous to the idea of breaking one of the four conditions of deadlock), corresponding to two schemes respectively:

  • Incremental update: record the reference relationship from black to white. After concurrent marking, take black as the root and rescan; A direct survival
  • Original snapshot SATB (snapshot at the beginning): record the gray to white dereference relationship. After concurrent marking, take gray as the root and rescan; B is gray, and finally becomes black, surviving. Note that if there is no step 3, B and C become floating garbage

16. Classic garbage collector

Schematic diagram of matching use:

17,Serial, SerialOld

Principle: when GC is triggered due to insufficient memory, all user threads will be suspended, and the new generation will be marked and copied in a single way. The old generation will be marked and sorted out, and the user threads will be restored after collection

Advantages: the whole process STW is simple and efficient

Disadvantages: STW duration is positively related to the number of heap objects, and GC threads can only use 1 core, which cannot be accelerated

Scenario: single core CPU with less available memory (such as 100MB), the only option before JDK1.3

18,ParNew

Principle: the Serial implementation of multi-threaded parallel version can effectively reduce the STW duration; The number of threads is the same as the number of cores by default and can be configured

Scenario: before JDK7, it was used with the old CMS recycler

19,Parallel, Parallel Old

Garbage collection has two goals that are usually not both

  • Low delay: STW duration, fast response; Allow high-frequency and transient GC, such as reducing Cenozoic space and accelerating collection delay (throughput reduction)
  • High throughput: high user thread time / (user thread time + GC thread time) and low total GC time; Allow low frequency, single long time GC, (delay increase)

Principle: similar to ParNew, both are parallel recycling, and three options are added (tend to improve throughput)

  • -20: Maxgcpausetime: controls the maximum delay
  • -20: Gctimeratio: control throughput (99% by default)
  • -20: + useadaptive sizepolicy: enable adaptive policy to automatically adjust the memory ratio of Eden and two Survivor areas - XX:SurvivorRatio, promotion threshold of the elderly generation - XX: pretenusizethreshold

20,CMS

CMS: Concurrent Mark Sweep, i.e. Concurrent Mark Sweep, mainly has four stages

  • initial mark: STW collects GC Roots quickly
  • concurrent mark: detect the reference chain from GC Roots and mark recyclable objects; Execute concurrently with the user thread, and avoid false recycling through incremental update
  • remark: STW reanalyzes GC Roots collected by incremental updates
  • Concurrent sweep: concurrent sweep recyclable objects

Advantages: the time of two STW s is much shorter than that of concurrent marking, and the delay is greatly reduced compared with the first three collectors

shortcoming

  • CPU sensitivity: if the number of cores is small (< 4core), concurrent marking will occupy a lot of CPU time, resulting in a sudden drop in throughput
  • Unable to process floating garbage: - XX: cmsinitiatingoccupancyfraction (default 92%), specify the threshold to trigger CMS GC; During concurrent marking and concurrent cleaning, the user thread will generate floating garbage (reference recyclable objects and generate new objects). If the proportion of floating garbage exceeds - XX: cmsinitiatingoccupancyfraction; If too much floating garbage is generated at the same time of GC, resulting in insufficient memory of the older generation, CMS concurrency failure will occur and degenerate into Serial Old to execute Full GC, resulting in a sudden increase in delay
  • Unable to avoid memory fragmentation: - XX:CMSFullGCsBeforeCompaction (default 0) specifies to defragment the memory fragments of the old age before Full GC every time

21,G1

Features: local recycling based on region memory layout; GC delay target configurable; No memory fragmentation

G1

Previous recycler

Heap memory partition mode

Multiple regions of the same size. The generational roles of each region are not fixed. Switch between Eden, survivor and old as needed

Fixed size, fixed number of generational areas

Recovery target

Regions with high recycling value are dynamically composed of recycling collections

Cenozoic, whole heap memory

Cross generation reference: in addition to using the card table to mark whether each card page is dirty, each region also uses the hash table to record which regions each card page is being referenced by. Through this "two-way pointer" mechanism, the Old area can be found directly to avoid full scanning (the memory overhead of G1 itself)

G1 GC has 3 stages (refer to its GC log)

  • Cenozoic GC: Eden area proportion over threshold trigger; Mark the living objects and copy them to the Survivor area. There may be objects in it that will be promoted to the Old area
  • When the proportion of GC: Old area reaches the threshold, it is triggered to perform label sorting
  • Initial tag: enumerate GC Roots, which has been completed during the new generation GC
  • Concurrency flag: perform reachability analysis concurrently, and use SATB to record reference changes
  • Re labeling: SATB analysis to avoid false recycling
  • Filter recycling: the regions are filtered according to the recycling value and time cost to form a recycling set. STW copies the living objects to the empty regions and cleans up the old regions to complete the recycling
  • Mixed GC

Parameter control (document: HotSpot GC Tuning Guide)

Parameters and default values

describe

‐XX:+UseG1GC

Manually enable G1 before JDK9

-XX:MaxGCPauseMillis=200

Expected maximum GC pause time; It should not be too small to avoid frequent GC caused by less memory recovery each time

-XX:ParallelGCThreads=N

The number of STW parallel threads. If the number of available cores m < 8, then N=1; otherwise, the default is N=M*5/8

-XX:ConcGCThreads=N

The number of concurrent threads in the concurrency phase is 1 / 4 of ParallelGCThreads by default

-XX:InitiatingHeapOccupancyPercent=45

If the region of the elderly generation accounts for more than 45%, the GC of the elderly generation will be triggered

-XX:G1HeapRegionSize=N

Single region size, 1~32MB

-XX:G1NewSizePercent=5, -XX:G1MaxNewSizePercent=60

The Cenozoic region accounts for a minimum of 5% and a maximum of 60% of the whole stack. If it exceeds, the Cenozoic GC will be triggered

-XX:G1HeapWastePercent=5

The proportion of wasted heap memory is allowed. If the recyclable memory is less than 5%, mixed recycling will not be carried out

-XX:G1MixedGCLiveThresholdPercent=85

The surviving objects of the elderly generation account for more than 85%, and the recovery value is low, so they will not be recovered temporarily

-XX:G1MixedGCCountTarget=8

Mixed recovery times in a single collection

22. Memory allocation strategy

Using Serial Collector - XX:+UseG1GC demo

1. Priority distribution of objects in Eden District

The new objects are allocated in Eden district. If the space is insufficient, miner GC will be triggered, and the surviving objects will be copied to To Survivor. If the memory is still insufficient, they will be transferred to the elderly district through the allocation guarantee mechanism. If the memory is still insufficient, OOM will be started

byte[] buf1 = new byte[6 * MB];
byte[] buf2 = new byte[6 * MB]; // 4MB left in 10MB eden area, insufficient space, trigger minor GC

// java -verbose:gc -Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:+UseSerialGC com.ch03.Allocation
// After minor gc, the memory of the new generation was reduced from 6M to 0.2M, the surviving objects were moved to the old area, and the total heap memory was still 6MB
[GC (Allocation Failure) [DefNew: 6823K->286K(9216K), 0.002 secs] 6823K->6430K(19456K), 0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 6513K 
eden space 8192K,  76% used // buf2
from space 1024K,  28% used
to   space 1024K,   0% used 
 tenured generation   total 10240K, used 6144K
 the space 10240K,  60% used // buf1

2. Large objects directly enter the elderly area

For serial and parnew, large objects (continuous memory) exceeding the threshold of - XX: pretenusizethreshold can be configured and allocated directly in the older generation to avoid triggering minor gc, resulting in a large number of memory replication operations by Eden and Survivor

byte[] buf1 = new byte[4 * MB];

// java -verbose:gc -Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:+UseSerialGC
// -20: Pretenuresizethreshold = 3145728 com. CH03. Allocation / / 3145728, i.e. 3MB
Heap
 def new generation   total 9216K, used 843K 
eden space 8192K,  10% used 
from space 1024K,   0% used 
to   space 1024K,   0% used 
 tenured generation   total 10240K, used 4096K 
 the space 10240K,  40% used // buf1

3. The long-term surviving objects enter the elderly generation

The 4bit age field in the object header stores the current GC generation age of the object. When the threshold - XX:MaxTenuringThreshold (15 by default, that is, the maximum value of the age field) is exceeded, it will be promoted to the old age. You can use - XX:+PrintTenuringDistribution to observe the generation distribution

byte[] buf1 = new byte[MB / 16];
byte[] buf2 = new byte[4 * MB];
byte[] buf3 = new byte[4 * MB]; // Trigger minor gc
buf3 = null;
buf3 = new byte[4 * MB];

// java -verbose:gc -Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:+UseSerialGC 
// -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution com.ch03.Allocation
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     359280 bytes,     359280 total
: 4839K->350K(9216K)] 4839K->4446K(19456K), 0.0017247 secs] 
// So far, buf1 has survived the first collection, age=1
[GC (Allocation Failure) [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1): 4446K->0K(9216K)] 8542K->4438K(19456K)] 
Heap
 def new generation   total 9216K, used 4178K 
eden space 8192K,  51% used 
from space 1024K,   0% used // buf1 was promoted in advance in the second round of collection
to   space 1024K,   0% used 
 tenured generation   total 10240K, used 4438K 
 the space 10240K,  43% used 

4. Dynamic judgment of generation age

-20: Maxtenuringthreshold is not the minimum hard threshold for promotion. When the number of objects of the same age in Survivor exceeds 50%, objects greater than or equal to this age will be promoted automatically, even if it has not reached the threshold

5. Space allocation guarantee

The old age is the guarantee area of To Survivor area. When the total size of surviving objects in Eden + From Survivor exceeds To Survivor, it will try to store them in the old age. After JDK6, as long as the continuous space of the old age is greater than the total size of the new generation objects or the average size of the previous promotion, only Minor GC will be performed, otherwise Full GC will be performed

23. Class file structure

Class file realizes language independence and JVM realizes platform independence. Refer to Java virtual machine specification

A Class file describes the explicit definition of a Class or interface. The content of the file is a group of binary streams in 8 bytes. There is no separator between data items. Data items exceeding 8 bytes are stored after being segmented by big endian. There are two types of data items:

  • Unsigned number: describes the basic type; Use U1, U2, U4 and U8 to represent the unsigned number of 1,2,4 and 8 bytes in length respectively; Store digital values, index sequence numbers, UTF-8 encoded values, etc
  • Table: composite type composed of unsigned numbers and nested other tables; Agreement_ info suffix; Store field types, method signatures, and so on

24. Structure definition

25. Grammar

Reference document: The class File Format

ClassFile {
u4             magic;         // magic number
u2             minor_version; // Version number
u2             major_version;
u2             constant_pool_count; // Constant pool
cp_info        constant_pool[constant_pool_count-1];
u2             access_flags;     // Class access tag
u2             this_class;       // Fully qualified name of this class
u2             super_class;      // Single parent
u2             interfaces_count; // Multiple interfaces
u2             interfaces[interfaces_count];
u2             fields_count;  // Field table
field_info     fields[fields_count];
u2             methods_count; // Method table
method_info    methods[methods_count];
u2             attributes_count; // Class properties
attribute_info attributes[attributes_count];
}
  • Magic: magic number, simply identify *. class file, and the value is fixed as 0xCAFEBABE
  • minor_version, major_version: the minor and major version numbers of the Class file
  • constant_pool_count: constant pool size + 1
  • constant_pool: constant pool. The index starts from 1. The value of 0 is reserved, indicating that no constant in any constant pool is referenced; Constants fall into two categories
  • Literal quantity: such as UTF8 string, int, float, long, double and other numeric constants
  • Symbol reference: there are 17 existing constants such as fully qualified names of classes and interfaces, field names and descriptors, method types and descriptors. Except that u1 tag prefix is used to identify constant types, the structures of constants are different from each other. The common ones are:
  • CONSTANT_Utf8_info: save the string encoded by UTF8
CONSTANT_Utf8_info {
  u1 tag;           // The value is 1
  u2 length;        // bytes array length, u2 the maximum value is 65535, that is, the literal amount of a single string does not exceed 64KB
  u1 bytes[length]; // Byte array of variable length
}
  • CONSTANT_Class_info: represents a symbolic reference to a class or interface
CONSTANT_Class_info {
  u1 tag;        // The value is 7
  u2 name_index; // Utf8 pointing to the fully qualified class name_ Info / / there is a hierarchical combination relationship between constants
}
  • CONSTANT_Fieldref_info, CONSTANT_Methodref_info, CONSTANT_NameAndType_info: member variables, member methods, and their type descriptors
CONSTANT_Fieldref_info {
  u1 tag;                 // The value is 9
  u2 class_index;         // Category
  u2 name_and_type_index; // Field name, type descriptor
}
CONSTANT_Methodref_info {
  u1 tag;                 // The value is 10
  u2 class_index;         // Category
  u2 name_and_type_index; // Method name, signature descriptor
}
CONSTANT_NameAndType_info {
  u1 tag;              // The value is 12
  u2 name_index;       // The name of the field or method
  u2 descriptor_index; // type descriptor 
}

Only the structures of five constants are listed above. It can be seen that the hierarchical relationship between constants is described by combination

  • access_flags: the access flag of the class. It has 16 bits, and each flag corresponds to one bit, such as ACC_PUBLIC corresponds to 0x0001, indicating that the class is modified by public; The other 8 marks refer to Opcodes.ACC_XXX
  • this_class, super_class: the class that points to this class and the only parent Class_info Symbolic Constant
  • interface_count, interfaces: multiple interface information describing the implementation of this class
  • fields_count, fields: field table; Describes the number and details of class fields and member variables
field_info {
u2             access_flags;     // Access tags such as scope, static,final,volatile, etc
u2             name_index;       // Field name
u2             descriptor_index; // type descriptor 
u2             attributes_count; // Property sheet for field
attribute_info attributes[attributes_count];
}

The type descriptor simplifies the description of the data type of the field, the parameter list and return value of the method. The relationship with the type in Java is as follows:

  • Basic types: Z:boolean, B:byte, C:char, S:short, I:int, F:float, D:double, J:long
  • Void and reference type: V:void
  • Reference type: L:, Replace. In the class name with /, and add; Delimiter, such as Ljava/lang/Object described as Object class;
  • Array type: each dimension is represented by a prefix [example: the corresponding descriptor of boolean regionMatch(int, String, int, int) is (ILjava/lang/String;II)Z
  • methods_count, methods: method table; completely describes the modifier, parameter list, return value and other signature information of each member method
method_info {
u2             access_flags;     // Access tag
u2             name_index;       // Method name
u2             descriptor_index; // Method Descriptor 
u2             attributes_count; // Method property sheet
attribute_info attributes[attributes_count];
}

Field table and method table can have multiple attribute tables, such as constant field table, method bytecode instruction table, method exception table, etc. attribute template:

attribute_info {
u2 attribute_name_index;   // Attribute name
u4 attribute_length;       // Attribute data length
u1 info[attribute_length]; // For other fields, the structure of each attribute is different
}

There are 20 + attributes, and only three common attributes are recorded here

  • Code attribute: stores the bytecode instruction compiled by the method
Code_attribute {
  u2 attribute_name_index; // Property name, and the Utf8_info value pointed to is fixed as "Code"
  u4 attribute_length;     // Length of remaining bytes
  u2 max_stack;  // The maximum depth of the operand stack, which is the depth of the operand stack in the stack frame of this method
  u2 max_locals; // The size of the local variable table in slot variable slot, storing the hidden parameter this, argument list, catch parameter, local variable, etc
  u4 code_length;       // Total bytecode instruction length
  u1 code[code_length]; // The JVM instruction set size is 200 +, and the number of a single instruction is described by u1
  u2 exception_table_length; // The exception table describes the exception generated by each instruction interval in the method and its handler address
  {   u2 start_pc;   // An exception of type catch_type will be thrown within the scope of the [start_pc, end_pc) instruction
      u2 end_pc;   
      u2 handler_pc; // If this exception is thrown, goto is executed at the handler_pc
      u2 catch_type;
  } exception_table[exception_table_length];
  u2 attributes_count; // The Code attribute is its own attribute
  attribute_info attributes[attributes_count];
}
  • LineNumberTable attribute: records the correspondence between the line number of Java source Code and the line number of bytecode. It is used to display the line number corresponding to the stack and other information when throwing exceptions. Can be a child of the Code attribute
LineNumberTable_attribute {
  u2 attribute_name_index; u4 attribute_length;
  u2 line_number_table_length;
  {   u2 start_pc;     // Start position of bytecode instruction interval
      u2 line_number;  // Corresponding source code line number
  } line_number_table[line_number_table_length];
}
  • LocalVariableTable property: records the variable name of the local variable in the Java method and the corresponding relationship with the variable in the stack frame local variable table. It is used to retain the meaningful variable name of each method
LocalVariableTable_attribute {
  u2 attribute_name_index; u4 attribute_length;
  u2 local_variable_table_length;
  {   u2 start_pc; // Bytecode offset at the beginning of the local variable life cycle
      u2 length;   // Bytecode length covered by the backward lifecycle
      u2 name_index;       // Variable name
      u2 descriptor_index; // type descriptor 
      u2 index; // The slot index in the corresponding local variable table
  } local_variable_table[local_variable_table_length];
}

For other properties, refer directly to the JVM documentation

26. Example

Source code: com/cls/Structure.java

package com.cls;

public class Structure {
public static void main(String[] args) {
  System.out.println("hello world");
}
}

After javac -g:lines com/cls/Structure.java is compiled, refer to the correct result obtained by javap decompilation, od -x --endian=big Structure.class to obtain the hexadecimal representation of the content of the class file. The interpretation is as follows:

cafe babe # 1. U4 magic number, identifying class file type
0000 0034 # 2. U2, U2 version number, 52 JDK8 

# 3. Constant pool
---1---
001f # u2 constant_pool_count, 31 items (counting from 1, 0 reserved)
0a      # u1 tag,10,Methoddef_info, member method structure 
0006    # u2 index,6,Category Class_info Number in constant pool   ## java/lang/Object
0011    # u2 index,17,This method NameAndType number             ## <init>:()V

---2---
09      # 9,Fileddef_info, member variable structure
0012    # u2 index,18,Category Class_info number     ## java/lang/System
0013    # u2 index,19,This field NameAndType number    ## out:Ljava/io/PrintStream

---3---
08      # 8,String_info, string
0014    # u2 index,20,Literal number     ## hello world

---4---
0a 
0015    # 21    ## java/io/PrintStream
0016    # 22    ## println:(Ljava/lang/String;)V

---5---
07      # Class_info, fully qualified class name
0017    # u2 index,23,Literal number     ## com/cls/Structure

---6---
07      # 7,Class_info, class reference
0018    # 24    ## java/lang/Object

---7---
01      # Utf8_info, UTF8 encoded string
0006    # u2 length, 6, string length
3c 69 6e 69 74 3e # literal values     ## "<init>"

---8-16---
01 0003 282956                                          ## "()V"
01 0004 436f6465                                        ## "Code"
01 000f 4c696e654e756d6265725461626c65                  ## "LineNumberTable"
01 0004 6d61696e                                        ## "main"
01 0016 285b4c6a6176612f6c616e672f537472696e673b2956    ## "([Ljava/lang/String;)V"
01 0010 4d6574686f64506172616d6574657273                ## "MethodParameters"
01 0004 61726773                                        ## "args"
01 000a 536f7572636546696c65                            ## "SourceFile"
01 000e 5374727563747572652e6a617661                    ## "Structure.java"

---17---
0c      # 12. NameAndType, name and type descriptor
0007    # u2 index,7,Field or method name area quantity number    ## <init>
0008    # u2 index,8,Field or method structure number       ## ()V

---18---
07 0019 # 25    ## java/lang/System

---19---
0c
001a 001b   # 26:27    ## out:Ljava/io/PrintStream;

---20---
01 000b 68656c6c6f20776f726c64    ## "hello world"

---21--
07 001c # 28    ## java/io/PrintStream

---22--
0c
001d 001e   # 29:30                             ## println:(Ljava/lang/String;)V

---23-31---
01 0011 636f6d2f636c732f537472756374757265          ## "com/cls/Structure"
01 0010 6a6176612f6c616e672f4f626a656374            ## "java/lang/Object "
01 0010 6a6176 612f 6c61 6e67 2f53 7973 7465 6d     ## "java/lang/System"
01 0003 6f7574                                      ## "out"
01 0015 4c6a6176612f696f2f5072696e7453747265616d3b  ## "Ljava/io/PrintStream;"
01 0013 6a6176612f696f2f5072696e7453747265616d      ## "java/io/PrintStream"
01 0007 7072696e746c6e                              ## "println"
01 0015 284c6a6176612f6c616e672f537472696e673b2956  ## "(Ljava/lang/String;)V"

0021 # 4. u2,access_flags                           ## ACC_PUBLIC | ACC_SUPER
0005 # 5. u2, this_class,5                           ## --5.Class_info--> com/cls/Structure
0006 # 6. u2, super_class, 6                         ## --6.Class_info--> java/lang/Object 
0000 # 7. u2, interface_count, 0
0000 # 8. u2, fields_count, 0

0002 # 9. methods count, 2
  # Method 1
0001    # u2, access_flags, ACC_PUBLIC                 
0007    # u2, name_index, 7                         ## <init> 
0008    # u2, descriptor_index, 8                   ## ()V                          
0001    # u2, attribute_count, 1                             
0009        # u2, attribute_name_index, 9           ## Code attribute
0000 001d   # u4, attribute_length, 30 
0001        # u2, max_stack, 1                                
0001        # u2, max_locals, 1                            
0000 0005  # u4, code_array_length, 5                        
2a               # u1, aload_0                       ## Put the variable this in the 0th slot on the stack 
b7   0001        # u1, invokespecial                 ## Execute < init > inherited from Object
b1               # u1, return                        ## Return void
0000        # u2, exception_table_length, 0          ## exception table is empty, no exception
0001        # u2, attributes_count, 1                ## Sub attribute of Code attribute itself
000a            # 10                                    ## LineNumberTable property
0000 0006       # 6
0001            # u2, line_number_table_length, 1
0000                # u2, start_pc, 0
0003                # u2, line_number, 3
  # Method 2
0009    # access_flags                               ## ACC_PIBLIC | ACC_STATIC
000b    # name_index, 11                             ## main
000c    # descriptor_index, 12                       ## ([Ljava/lang/String;)V
0002    # attribute_count, 2
0009        # attribute_name_index, 9                ## Code
0000 0025  # attribute_length, 37
0002        # max_stack, 2 
0001        # max_locals, 1
0000 0009  # code_array_length, 9
b2   0002       # getstatic, 2                       ## Field: java/lang/System.out:Ljava/io/PrintStream; //  Load static object variables
12   03         # ldc, 3                             ## String: "hello world" / / put constant parameters on the stack
b6   0004       # invokevirtual, 4                   ## Method: java/io/PrintStream.println:(Ljava/lang/String;)V / / execute the method
b1              # return
0000        # exception_table_length, 0
0001        # attributes_count, 1
000a        # 10                                     ## LineNumberTable
0000 000a   # 10
0002            # line_number_table_length, 2
0000 0005           # 0 -> 5
0008 0006           # 8 -> 6

27. Bytecode instruction

The JVM designs an instruction set for the operand stack. Each instruction is represented by a 1-byte opcode followed by 0 or more operands. Refer to Java bytecode instruction lists for the instruction set list

  • For most instructions related to data types, their opcode symbols will be prefixed with type. For example, the i prefix represents the operation int, and the remaining correspondence is b:byte, c:char, s:short, f:float, d:double, l:long, a:reference
  • Because the instruction set size is limited (256), Boolean, byte, char and short will be converted to int operation

Byte codes can be roughly divided into six categories:

  • Load and store instructions: load variables from the local variable table slot to the top of the operand stack, and store them in reverse
// Load slot 0, 1, 2, 3 and N to the top of the stack. T represents the type abbreviation prefix, which can take I, l, F, D and a
Tload_0, Tload_1, Tload_2, Tload_3, Tload n
// Write stack top data back to the specified slot
Tstore_0, Tstore_1, Tstore_2, Tstore_3, Tstore n
// Load constant values in different ranges to the top of the stack. Since 0 ~ 5 constants are too common, there are separate corresponding instructions, and ldc loads ordinary constants
bipush, sipush, Tconst_[0,1,2,3,4,5], aconst_null, ldc
  • Operation instruction
Tadd, Tsub, Tmul, Tdiv, Trem     // Arithmetic operations: addition, subtraction, multiplication, division, remainder
Tneg, Tor, Tand, Txor            // Bit operation: inverse, or, and, XOR
dcmpg, dcmpl, fcmpg, fcmpl, lcmp // Comparison operation: suffix g is greater, and l is less than
iinc                             // Local auto increment operation, used with iload
  • Cast instruction: when narrowing to type T (length n), other bits except the lower N bits will be directly discarded, which may lead to data overflow, uncertain sign, and loss of precision when converting floating-point numbers to integers
i2b // int -> byte
i2c, i2s; l2i, f2i, d2i; d2l, f2l; d2f
  • Object creation and access instructions: class instances and arrays are objects. The creation and access instructions are different due to different storage structures
new                                      // Create class instance
newarray, annewarray, multianewarry      // Create basic type array, reference type array and multidimensional reference type array
getfield, putfield; getstatic, putstatic // Read / write class instance fields; Read / write static fields
Taload, Tastore; arraylength             // Read and write array elements; Calculate array length
instanceof; checkcast                    // Verify whether the object is a class instance; Perform cast
  • Operand stack management instruction
pop, pop2       // Pop up stack top 1 and 2 elements
dup, dup2; swap // Copy 1 and 2 elements at the top of the stack and put them back on the stack; Swap the top two elements of the stack
  • Control transfer instruction: if the judgment condition is true, jump to the specified instruction line (modify the PC point)
if_<icmpeq,icmpne;icmplt,icmple;icmpgt,icmpge;acmpe,acmpne> // Integer comparison, reference equality judgment
if<eq,lt,le,gt,ge,null,nonnull>                             // Used with other types of comparison instructions
  • Method call and return instructions
invokevirtual   // Dispatch according to the actual type of the object and call the corresponding method (such as method rewriting after inheritance) 
invokespecial   // Call special methods, such as < cint > () V, < init > () V and other initialization methods, private methods and parent methods
invokestatic    // Calling a static method of a class
invokeinterface // Call interface methods (class objects that implement interfaces but are declared as interface types, call methods)
invokedynamic   // TODO
Treturn, return // Returns the specified type and void
  • Exception handling instruction: athrow throws an exception, and exception is used for exception handling_ Table description
  • Synchronization instruction: the synchronized object lock is implemented by monitorenter, monitorexit and the object's monitor lock

28. Class loading

29. Class loading process

1. Loading

Principle: delegate ClassLoader to read the Class binary byte stream, load it into the memory of the method area, and generate the corresponding java.lang.Class objects in the heap memory for mutual reference

2. Verification

Verify byte stream to ensure compliance with Class file format, perform semantic analysis to ensure compliance with Java syntax, and verify the legitimacy of bytecode instructions

3. Preparation

Allocate the memory of class variable (static) in the heap and initialize it to zero value. It is not yet in the initialization stage of performing the assignment of putstatic instruction, except for the static constant attribute:

public class ClassX {
final static int n = 2;          // The value of the constant is known at compile time, the assignment is completed at the preparation stage, and the value is stored in ConstantValue
final static String str = "str"; // The same is true for string static constants
}

static final java.lang.String str;
descriptor: Ljava/lang/String;
flags: ACC_STATIC, ACC_FINAL
ConstantValue: String str

4. Analysis

Replace the symbol reference (Class_info, Fieldref_info, Methodref_info) in the constant pool with a direct reference (memory address)

5. Initialization

javac will combine the static variable assignment and static statement blocks in the class from top to bottom to generate a class constructor () V, which will be executed in the initialization stage. The JVM ensures thread safety in the execution of this method; Note that there are only six case s specified by the JVM that will immediately trigger class initialization

public class ClassX {
static {
  println("main class ClassX init"); // 1. The main class of main () is always initialized first
}

public static void main(String[] args) throws Exception {
  // Class initialization is triggered for the first time
  // SubX b = new SubX();  // new object / / 2. new, getsatic, putstatic, invokestatic instructions
  // println(SuperX.a);    //  Read and write the static variable of the class, or call the static method 
  // println(SubX.c);      // 3. Child class initialization will trigger parent class initialization
  // println(SubX.a);      //     Subclasses accessing the static variables of the parent class will only trigger the initialization of the parent class
  
  // Class initialization is not triggered
  // println(SubX.b);      // 1. Static constants of access class (basic type, string literal)
  // println(SubX.class);  // 2. Access class objects
  // println(new SubX[2]); // 3. Create an array of classes
}
}

class SuperX {
static int a = 0;
static {
  println("class SuperX initiated");
}
}

class SubX extends SuperX {
final static double b = 0.1;
static boolean c = false;
static {
  println("class SubX initiated");
}
}

30. Class loader

Hierarchical relationship

Parental delegation mechanism

  • Principle: when a class loader receives a request to load a class, it will first delegate the upper parent class loader to load it layer by layer. When the parent class loader can't load this class layer by layer, the class loader will try to load it by itself; This model ensures that java.lang.Object classes, such as those in rt.jar, must be loaded by the Bootstrap class loader no matter what kind of loader at the bottom. This is the only one in the JVM to ensure consistency
  • realization
// java/lang/ClassLoader
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 1. First check whether your loader has loaded this class
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 2. The upper layer is delegated to the upper layer to load
                    c = parent.loadClass(name, false);
                } else {
                    // 3. If there is no superior, it is delegated to Bootstrap to load
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // class does not exist
            }

            if (c == null) {
                // 4. Find classes in your classpath. The user-defined ClassLoader has customized the search rules
                long t1 = System.nanoTime();
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

31. Bytecode execution engine

32. Runtime stack frame structure

public static void main(String[] args) {
int a = 1008611;
int b = ++a;
}

Corresponding runtime stack frame structure:

  • Local variable table: the size is determined at the compile time. It is used to store arguments and local variables. The minimum unit is the variable slot with the size of 32 bit s
  • Long and double types are divided into two slot s and read and write at the same time (single thread operation, wireless path security issues)
  • When a method is called by a class object, slot 0 is fixed as the reference of the current object, that is, the implicit argument of this
  • The variable slot has reuse optimization. When the pc instruction exceeds the scope of the variable in a slot, the slot will be reused by other variables
public static void main(String[] args) {
  {
      byte[] buf = new byte[10 * 1024 * 1024];
  }
  System.gc();  // buf is also in slot 0 of the local variable table and cannot be recycled as a GC Root
  // int v = 0; //  Variable V reuses slot 0 and gc takes effect
  // System.gc(); 
  • Operand stack: the maximum depth is determined at compile time, and bytecode instructions are executed in combination with local variable table by entering, executing and exiting the stack
  • Return address: when a return family instruction is encountered, the normal call is completed. When an exception occurs, but there is no corresponding handler in the exception table, the exception call is completed; After exiting the upper layer method normally, if there is a return value, it will be pushed into the stack, and the program counter will be restored to the next instruction of the method call instruction

33. Method call

34. Virtual method and non virtual method

Non virtual method: a method known at compile time (uniquely determined before the program runs) and immutable at run time. In the class loading stage, the symbolic reference of the method will be resolved to a direct reference. There are 5 types:

  • Static method (uniquely associated with class): invokestatic call
  • Private method (not accessible externally), constructor method, parent method: invokespecial call
  • final method (cannot be inherited): called by invokevirtual (historical reason)
public class StaticResolution {
 public static void doFunc() {
  System.out.println("do func...");
 }
 public static void main(String[] args) {
  StaticResolution.doFunc();
 }
}

stack=0, locals=1, args_size=1    // The calling version of the static method stores the bytecode parameters in the form of constants at compile time
0: invokestatic  #5           // Method doFunc:()V
  3: return

Virtual method: the directly referenced method needs to be dynamically determined at runtime and called by invokevirtual and invokeinterface

35. Static dispatch and dynamic dispatch

Background: methods can be overloaded (different parameter types or numbers) and overridden (overridden after subclass inheritance)

Dispatch: an object can be declared as a class, parent class, implemented interface and other types. When an object is used as an argument or calls a method, the version of the method to be called can be determined according to its static type or actual type, and then its direct reference can be determined. This process is the dispatch of methods

2 types of reference variable

  • Static type: the declared type of a variable. It will not change. It can be known at compile time
  • Actual type: the object pointed to by the variable can be replaced and may be modified at run time

Static Dispatch

  • Principle: when a method is overloaded, it depends on the static type of the parameter to determine which overloaded version of the method to use
  • Features: in the compilation stage, javac determines the overloaded version of the type "with the highest matching degree" as the parameter of invokevirtual
public class StaticDispatch {
 static abstract class Human {}
 static class Man extends Human {}
 static class Woman extends Human { }

 public void f(Human human) {System.out.println("f(Human)");}
 public void f(Man man) {System.out.println("f(Man)");}
 public void f(Woman woman) {System.out.println("f(Woman)");}

 public static void main(String[] args) {
  Human man = new Man();     // Static types are Human
  Human woman = new Woman(); // The actual types are man and woman
  StaticDispatch sd = new StaticDispatch();
  sd.f(man);   // f(Human) // invokevirtual #13 // Method f:(Lcom/ch08/StaticDispatch$Human;)V
  sd.f(woman); // f(Human) / / the overloaded version is determined at the compile time and written into the bytecode
 }
}

Dynamic dispatch

  • Principle: when overriding a method, it depends on the actual type of the Receiver object to determine which class version of the method to use
  • Features: when it occurs at runtime, it depends on the invokevirtual instruction to determine the version of the calling method, so as to realize polymorphism. The parsing process is

Note: class method lookup is a high-frequency operation. The JVM will create a virtual method table vtable for the class in the method area to realize fast method lookup

public class DynamicDispatch {
static abstract class Human {
    protected abstract void f();
}

static class Man extends Human {
    @Override
    protected void f() {
        System.out.println("Man f()");
    }
}

static class Woman extends Human {
    @Override
    protected void f() {
        System.out.println("Woman f()");
    }
}

public static void main(String[] args) {
    Human man = new Man(); // Although static types are Human
    Human woman = new Woman();
    man.f();   // Man f()   // invokevirtual #6 // Method com/ch08/DynamicDispatch$Human.f:()V
    woman.f(); // Woman f() / / although the parameters of bytecode instructions are symbolic references of statically typed methods
    man = new Woman();
    man.f(); // Woman f() / / however, invokevirtual will resolve to the direct reference of the actual class at runtime according to the actual type of Receiver
}
}

Note that the field read / write instructions getfield and putfield of the class do not have the dynamic dispatch mechanism of invokevirtual, that is, the fields with the same name of the subclass will directly overwrite the fields of the parent class. Example:

public class FieldHasNoPolymorphic {
static class Father {
    public int money = 1;
    public Father() {
        money = 2;
        showMoney();
    }
    public void showMoney() { System.out.println("Father, money = " + money); }
}

static class Son extends Father {
    public int money = 3; // The subclass field is assigned a zero value in the preparation phase of class loading
    public Son() { // The first line of subclass constructor is hidden by default, and super() is called
        money = 4;
        showMoney();
    }
    public void showMoney() { System.out.println("Son, money = " + money); }
}

public static void main(String[] args) {
    Father guy = new Son(); 
    System.out.println("guy, money = " + guy.money);
}
}

// Son, money = 0 / / the father class constructor executes Son::showMoney() dynamically
// Son, money = 4 / / access the latest and own money field in the son class constructor
// guy, money = 2 / / there is no dynamic allocation for reading and writing fields. Whoever the static type is, the field will be accessed

36. Single dispatch and multiple dispatch

The Receiver of the method and the parameters of the method are the quantities of the method. Selecting the target method according to one quantity is called single dispatch. Multiple quantities are required to determine the method, which is called multiple dispatch

  • The static dispatch mechanism allows the compiler to select the target method with the highest parameter matching for multiple overloaded methods at the compilation stage
  • When the dynamic dispatch mechanism is running, it relies on the actual type of Receiver and cooperates with invokevirtual to locate the unique instance method as the target method

To sum up, Java is a static multi dispatch and dynamic single dispatch language

Note: Chapters 10 and 11 talk about the front and back-end compilation of Java, and learn the bytecode implementation of common syntax sugar such as automatic packing. The rest will be learned together with dragon book when you have time; The contents of chapters 12 and 13 are highly coincident with Java Concurrency In Practice and other books, which will not be repeated here

37. Summary

After studying the deep understanding of JVM 3ed, I have preliminarily mastered the partition model of JVM memory area, the theory of GC algorithm and the principle of common recyclers, the interpretation of data items in Class file structure, Class loading process, method execution and dispatch, and gained a lot. However, most of them are theories. If you have the opportunity, you still need to study the source code implementation of openjdk:(

yinzige.com/2020/03/08/understanding-jvm-3ed/

Posted by windjohn on Fri, 03 Dec 2021 23:13:18 -0800