This is the 11th day of my participation in the August More Text Challenge. For details, see:August is more challenging
preface
This brings us to the second series, on garbage collection and optimization.
For the developers engaged in C and C++ program development, in the field of memory management, they are not only the “emperor” with the highest power, but also the working people engaged in the most basic work — not only have the “ownership” of every object, but also shoulder the maintenance responsibility of every object life from the beginning to the end.
For Java programmers, with the help of virtual machine automatic memory management, no need to write paired delete/free code for every new operation, no memory leaks and memory overruns, it looks like everything is fine with virtual machine memory management. However, because Java programmers have given control of memory to the Java Virtual Machine, when memory leaks and overflows occur, it can be extremely difficult to fix the problems without understanding how the virtual machine uses memory
1. Why garbage collection
Java objects stored in the JVM fall into two categories:
➷ is a transient object with a short life cycle. The creation and death of such objects are very fast. If the life cycle is short, it can be recycled in time.
➷ Another class of objects has a very long life cycle and in some extreme cases can be consistent with the life cycle of the JVM.
Short-lived objects are stored in the new generation, while long-lived objects are stored in the old age.
Let’s take a look at the following code to parse their allocation in memory:
public class Test { private static User user = new User(); public static void main(String[] args) throws InterruptedException { user.login(); for (int i = 0; i < 10; i++) { doSomething(); Thread.sleep(2000); } } public static void doSomething(){ Student stu = new Student(); stu.study(); } class User{public void login(){system.out.println (" login "); }; } class Student{ public void study(){ System.out.println("I'm studying"); }; }Copy the code
The static member variable user in the Test class is long-lived and is assigned to a new generation.
The doSomething() method is called 10 times in the main method through the for loop, and the Student() object is created in the method.
We first freeze the time after the first execution, and the memory allocation is:
That’s just after the first execution of the doSomething() method. What happens after 10 executions?
After doSomething() executes, the corresponding stack frame will bounce off the stack, and the local variables of the corresponding stack frame will be released and collected accordingly. The instance object in the heap will become a garbage object without reference:
When the last doSomething() method is executed and the stack frame is loaded, there will be 10 Student instance objects in the new generation of heap memory that have no address reference. If there are more garbage objects in the new generation, when the content space in the new generation is no longer available, A “Minor GC” is performed to collect garbage objects corresponding to the new generation:
The only memory left is the User object:
Of course, if our program has gone through 15 Minor GC’s, the objects that have not been collected will be managed in our old age; Our User instance object, for example, will not be reclaimed because it is always referred to by a static variable of the Test class.
Of course, if the space in the old era is full, it will also trigger garbage collection, which will clean up the useless garbage objects in the old era.
2. How to judge the object can be recycled
Next, let’s examine what rules our JVM follows to collect garbage objects when it triggers garbage collection. Which objects can be reclaimed and which objects cannot be reclaimed. Let’s start with an reachability analysis algorithm:
2.1 Reachability analysis algorithm
The memory management subsystem of the current mainstream commercial programming languages (Java, C#, and back to ancient Lisp) all uses Reachability Analysis to determine whether an object is alive. The basic idea of this algorithm is to use a series of root objects called “GC Roots” as the starting node set, start from these nodes, and search down according to the Reference relationship. The path taken in the search process is called “Reference Chain”. If there is no chain of references between an object and the GC Roots, or in graph theory the object is unreachable from the GC Roots, then the object is proved to be impossible to use.
The bottom line is: For each object, analyze who is referring to it, and then go up layer by layer to see if there is a GC Roots
So what is considered to be a GC Root object?
In the Java technology architecture, objects that can be fixed as GC Roots include the following:
- Objects referenced in the virtual machine stack (local variables in the stack frame), such as parameters, local variables, temporary variables, etc. used in the stack of methods invoked by each thread.
- An object referenced by a class static property in the method area, such as a Java class reference type static variable.
- An object that is a constant reference in the method area, such as a reference in the String Table.
- Objects referenced by JNI (commonly referred to as Native methods) in the Native method stack.
- References within the Java virtual machine, such as Class objects for basic data types, resident exception objects such as NullPointExcepiton, OutOfMemoryError, and system Class loaders.
- All objects held by the synchronized keyword.
- Jmxbeans that reflect the internals of the Java virtual machine, callbacks registered in JVMTI, local code caches, and so on
In our code:
public class Test { private static User user = new User(); public static void main(String[] args) throws InterruptedException { user.login(); for (int i = 0; i < 10; i++) { doSomething(); Thread.sleep(2000); }} public static void doSomething(){// Student stu = new Student(); stu.study(); }}Copy the code
** The local variable stu is a GC Roots, which is one of the most common cases. ** If the code is calling doSomething() and the local variable STu holds the instance object, the new generation space is full, and garbage collection occurs, the reachabability of the stU object will be analyzed. At this time, it is found that the stU object is held by the local variable GC Roots and cannot be collected.
Class static variables are also a common GC Roots. The user object in the above code is held by the user static variable and will not be collected when garbage collection occurs.
summary
Through this section, I believe you have understood why garbage collection is used and which objects can be collected and which objects are alive through the reachability algorithm. There are two common GC Roots that we want you to remember: 1 is our local variable reference, 2 is our class static variable reference. Objects referenced by GC Roots cannot be collected.
So what objects in Java can or cannot be reclaimed besides those referenced by GCRoots that cannot be reclaimed? In the next section, we’ll focus on Java reference types: the concepts of strong, soft, weak, and virtual references, as well as the finalization mechanism for objects.