1. Java memory model
When executing a program, the Java virtual machine divides the memory it manages into several data areas. The distribution of these data areas is shown in the figure below:
Program counter: a small memory area pointing to the currently executed bytecode. If the thread is executing a Java method, this counter records the address of the virtual machine bytecode instruction being executed. If the Native method is executed, the calculator value is empty.
Java virtual machine stack: threads are private, and their life cycle is consistent with threads. When each method is executed, a stack frame will be created to store information such as local variable tables, operand stacks, dynamic links, method exits, etc.
Local method stack: The functions are similar to the virtual machine stack, except that the virtual machine stack performs Java method services for the virtual machine, while the local method stack serves the Native method used.
Java heap: It is the largest piece of virtual machine management memory, shared by all threads, this area is used to store object instances, and almost all objects are allocated in this area. The Java heap is the main area of memory recycling. From the perspective of memory recycling, since most of the current collectors use generational collection algorithms, the Java heap can also be subdivided into: the new generation and the old generation. If it is subdivided a little, it can be divided into Eden space, From Survivor space, To Survivor space, etc. According to the Java virtual machine specification, the Java heap can be in a physically discontinuous space, as long as it is logically continuous.
Method area: Like Java, it is shared by various threads and is used to store data such as class information that has been loaded by the virtual machine, always on, static variables, code compiled by the instant compiler.
Runtime constant pool, runtime constant pool is part of the method area. In addition to class version, fields, methods, interfaces and other description information, there is also a constant pool in the Class file, which is used to store various literal and symbolic references generated during the compilation period. During runtime, new constants can be placed into the constant pool. The most commonly used is the intern() method of the String class. When a String instance calls intern, Java finds whether there are the same Unicode string constants in the constant pool. If there is, it returns its reference; if not, add a Unicode equal to the instance string and returns its reference.
2. How to determine the garbage object
There are several object instances stored in the Java heap. Before the garbage collector recycles the heap, it first needs to determine which objects are still "alive" and which have "dead", that is, objects that will not be used by any means.
Quote counting
The citation counting method is simple to implement and efficiently, and is a good algorithm in most cases. The principle is: add a reference counter to the object. Whenever there is a place to reference the object, the counter is increased by 1. When the reference fails, the counter is reduced by 1. When the counter value is 0, it means that the object is no longer used. It should be noted that the reference counting method is difficult to solve the problem of mutual reference between objects, and mainstream Java virtual machines do not use the reference counting method to manage memory.
Accessibility analysis algorithm
The basic idea of this algorithm is to search downward through a series of objects called "GC Roots" as the starting point, starting from these nodes. The path searched through is called a reference chain. When an object is not connected to GC Roots without any reference chain (in the words of graph theory, it is from GC Roots to this object that is unreachable), it is proved that this object is unavailable. As shown in the figure, although objects 5, object 6, and object 7 are related to each other, they are unreachable to GC Roots, so they will be judged to be recyclable objects.
In Java language, the following objects that can be used as GC Roots include:
Object referenced in the virtual machine stack (local variable table in the stack frame).
Object referenced by the static attribute of the class in the method area.
Object referenced by constants in the method area.
Objects referenced by JNI (that is, the general Native method) in the local method stack.
Now the question is, will the accessibility analysis algorithm have a circular reference problem between objects? The answer is yes, that is, there will be no problem of circular reference between objects. GC Root is a specially defined "starting point" outside the object graph and cannot be referenced by objects in the object graph.
To Die Or Not To Die
Even unreachable objects in the accessibility analysis algorithm are not "must die". At this time, they are temporarily in the "probation" stage. To truly declare an object dead, it must go through at least two marking processes: if the object finds that there is no reference chain connected to GC Roots after performing accessibility analysis, it will be marked for the first time and filtered. The filtering condition is whether it is necessary for this object to execute the finapze() method. When the object does not overwrite the finapze() method, or the finapze() method has been called by the virtual machine, the virtual machine regards both cases as "no need to execute". In the program, you can overwrite finapze() to create a "thrilling" self-salvation process, but this is only one chance.
/** * This code demonstrates two points: * 1. Objects can save themselves when they are GC. * 2. There is only one chance of self-rescue, because the finapze() method of an object will only be automatically called once by the system at most * @author zzm */ pubpc class FinapzeEscapeGC { pubpc static FinapzeEscapeGC SAVE_HOOK = null; pubpc void isApve() { System.out.println("yes, i am still apve :)"); } @Override protected void finapze() throws Throwable { super.finapze(); System.out.println("finapze mehtod executed!"); FinapzeEscapeGC.SAVE_HOOK = this; } pubpc static void main(String[] args) throws Throwable { SAVE_HOOK = new FinapzeEscapeGC(); //The object successfully saves itself for the first time SAVE_HOOK = null; System.gc(); //Because the finapze method has low priority, pause for 0.5 seconds to wait for it Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isApve(); } else { System.out.println("no, i am dead :("); } //The following code is exactly the same as the above, but this time the self-rescue failed. SAVE_HOOK = null; System.gc(); //Because the finapze method has a low priority, it pauses for 0.5 seconds to wait for it Thread.sleep(500); if (SAVE_HOOK != null) { SAVE_HOOK.isApve(); } else { System.out.println("no, i am dead :("); } } }The running result is:
Finapze mehtod executed! yes, i am still apve :) no, i am dead :(
Let's talk about citations
Whether it is judging the number of references of an object through a reference counting algorithm or determining whether the object's reference chain is reachable through an accessibility analysis algorithm, determining whether the object's survival is related to "reference". Before JDK 1.2, the definition of references in Java was very traditional: if the value stored in reference type data represents the starting address of another piece of memory, it is said that this piece of memory represents a reference. After JDK 1.2, Java expanded the concept of reference, and divided references into four types: Strong Reference, Soft Reference, Weak Reference, and Phantom Reference. The strength of these four types of reference gradually weakened in turn.
• Strong citation refers to references that are common in program code, such as "Object obj = new Object()". As long as the strong citation still exists, the garbage collector will never recycle the referenced object.
• Soft references are used to describe some useful but not necessary objects. For soft reference associated objects, these objects will be listed in the recycling scope for a second recycling before the system is about to have a memory overflow exception. If there is not enough memory for this recycle, a memory overflow exception will be thrown. After JDK 1.2, the SoftReference class is provided to implement soft references.
• Weak references are also used to describe non-essential objects, but their strength is weaker than soft references. Objects associated with weak references can only survive until the next garbage collection occurs. When the garbage collector works, objects that are only associated with weak references are collected regardless of whether the current memory is sufficient. After JDK 1.2, the WeakReference class is provided to implement weak references.
• Void quotes are also called ghost quotes or phantom quotes, and they are the weakest citation relationship. Whether an object has a virtual reference will not have any impact on its survival time at all, nor will it be possible to obtain an object instance through virtual reference. The only purpose of setting up virtual reference associations for an object is to receive a system notification when the object is recycled by the collector. After JDK 1.2, the PhantomReference class is provided to implement virtual references.
Example of soft reference usage:
package jvm;import java.lang.ref.SoftReference;class Node {pubpc String msg = "";}pubpc class Hello {pubpc static void main(String[] args) {Node node1 = new Node(); // Strong reference node1.msg = "node1";SoftReference<Node> node2 = new SoftReference<Node>(node1); // Soft reference node2.get().msg = "node2";System.out.println(node1.msg);System.out.println(node2.get().msg);}}The output result is:
node2node2
3. Typical garbage collection algorithm
1.Mark-Sweep (mark-clear) algorithm
This is the most basic garbage collection algorithm. The reason why it is said to be the most basic is that it is the easiest to implement and the simplest idea. The mark-clearing algorithm is divided into two stages: the marking stage and the clearing stage. The task of the marking stage is to mark all objects that need to be recycled, and the clearing stage is to recycle the space occupied by the marked objects. The specific process is shown in the figure below:
It can be easily seen from the figure that the mark-clearing algorithm is easier to implement, but there is a serious problem that it is easy to generate memory fragments. Too many fragments may cause the inability to find enough space when allocating space for large objects in the subsequent process, and triggering a new garbage collection action in advance.
2. Copying algorithm
In order to solve the shortcomings of the Mark-Sweep algorithm, the Copying algorithm was proposed. It divides available memory into two pieces of equal size by capacity, using only one piece at a time. When this piece of memory is used up, copy the still-living object to another piece, and then clean up the used memory space at once, so that memory fragmentation problems will not occur. The specific process is shown in the figure below:
Although this algorithm is simple to implement, efficient to run and not easy to generate memory fragmentation, it is expensive to use the memory space because the memory that can be used is reduced to half of the original one.
Obviously, the efficiency of the Copying algorithm has a lot to do with the number of surviving objects. If there are many surviving objects, the efficiency of the Copying algorithm will be greatly reduced.
3. Mark-Compact (mark-collation) algorithm
In order to solve the shortcomings of the Copying algorithm and make full use of the memory space, the Mark-Compact algorithm is proposed. The algorithm marks the same as Mark-Sweep, but after completing the mark, it does not directly clean up the recyclable objects, but moves all the living objects to one end and then cleans up memory outside the end boundary. The specific process is shown in the figure below:
4. Generational Collection algorithm
Generation collection algorithm is currently used by most JVM garbage collectors. Its core idea is to divide memory into several different regions according to the life cycle of the object's survival. Generally speaking, the heap area is divided into the old generation and the Young Generation. The characteristic of the old generation is that only a small number of objects need to be recycled every time the garbage is collected, while the characteristic of the new generation is that a large number of objects need to be recycled every time the garbage is collected. Then the most suitable collection algorithm can be adopted according to the characteristics of different generations.
At present, most garbage collectors adopt the Copying algorithm for the new generation, because in the new generation, most objects need to be recycled every time, that is, the number of operations that need to be copied is small, but in reality, the space of the new generation is not divided according to a ratio of 1:1. Generally speaking, the new generation is divided into a larger Eden space and two smaller Survivor spaces (usually 8:1:1). Each time the Eden space and one of the Survivor spaces are used, when recycling, the objects that still survive in Eden and Survivor are copied to another Survivor space, and then Eden and the Survivor spaces that have just been used are cleaned.
Because the old age is that only a small number of objects are recycled every time, the Mark-Compact algorithm is generally used.
The above brief analysis of Java memory model and garbage collection is all the content I share with you. I hope you can give you a reference and I hope you can support Wulin.com more.