JVM determine whether the object is alive

Almost all of the store heap object instance, the garbage collector before the recovery of the heap, the first thing is to determine which of these objects still "alive", those objects had been "dead" (ie, the object can not be used in any way )

A reference count algorithm (Reference Counting)

Add a reference to the object counter, every time they place a reference to it, the counter value plus 1; When referring to failure, the counter is decremented by one; any time counters for the object 0 is not possible to be used but the Java language. no choice of reference counting to manage memory, the most important reason is that it is very difficult to resolve circular dependencies between objects to each other's problems .

For example: In testGC, objects and object objA have objB field instance, the assignment objA.instance = objB.instance and objB.instance = objA.instance, in addition to two other objects no longer any reference to this fact both objects can no longer be accessed, but they GC details because they reference each other with each other, their reference count is not zero, then the reference count collection algorithm can not be recovered GC inform them print:. -XX: + PrintGCDetails

public class ReferenceCountingGC {

    public Object instance = null;

    public static void main(String[] args) {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA.instance = objB;
        objB.instance = objA;


        objA = null;
        objB = null;

        //假设在这行发生了gc,objA和objB是否被回收
        System.gc();
    }
}
复制代码

GC logs for:

[GC (System.gc()) [PSYoungGen: 334480K->4736K(334848K)] 597914K->270331K(1017536K), 0.0019879 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 2408K->0K(298688K)] [ParOldGen: 0K->3363K(682688K)] 3408K->3363K(981376K), [Metaspace: 3162K->3162K(1056768K)], 0.0050515 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 153088K, used 3947K [0x0000000715580000, 0x0000000720000000, 0x00000007c0000000)
  eden space 131584K, 3% used [0x0000000715580000,0x000000071595afc0,0x000000071d600000)
  from space 21504K, 0% used [0x000000071d600000,0x000000071d600000,0x000000071eb00000)
  to   space 21504K, 0% used [0x000000071eb00000,0x000000071eb00000,0x0000000720000000)
 ParOldGen       total 349696K, used 872K [0x00000005c0000000, 0x00000005d5580000, 0x0000000715580000)
  object space 349696K, 0% used [0x00000005c0000000,0x00000005c00da3b8,0x00000005d5580000)
 Metaspace       used 3169K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 346K, capacity 388K, committed 512K, reserved 1048576K
复制代码

GC log analysis:

GC (minor) log:

Full GC log:

After half analysis

FIG above control, PSYoungGen (PS refers Parallel Scavenge) GC log is Eden + FromSpace, the whole YoungGeneration to Eden + FromSpace + ToSpace.

We set the size of the new generation is 10240K, which includes ToSpace 9216K and 1024K size PSYoungGen size. Wherein, PSYoungGen of Eden: FromSpace to 8: 1,

This includes the 8192K and 1024K of Eden FromSpace.

Detailed log output:

Parameter is set to:

-XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8 -XX:NewSize=10M -XX:MaxNewSize=10M

Parameters explanation:

-XX: + PrintGCDetails Enable logging

-XX: -UseAdaptiveSizePolicy disable dynamic adjustment, so that you can work SurvivorRatio

-XX: SurvivorRatio = 8 disposed Eden: Survivior = 8

-XX: NewSize = 10M -XX: MaxNewSize = 10M to set the size of a whole new generation 10M

The default garbage collector is: Parallel Scavenge

In the run log results, we can see GC log contains "2408K-> 0k", years old from 2408K (about 2M, in fact, objA and objB) becomes a 512K, meaning that the virtual machine is not because the two objects refer to each other they do not recover, it also proves that the virtual machine is not incorporated by reference counting method to determine whether the surviving objects. objects can see into the old era, but we all know, when the object just created is assigned to the new generation that to enter old to new objA's need for the job, but here objA and objB has entered the old era. this is because the Java heap grows dynamically at the beginning heap small objects into the old rules and some years, when survior space when the size of the objects in the same generation and more than half survior space, the object will go directly to the old era.

Second, the reachability analysis algorithm (GC Roots Analysis): the mainstream with this judgment

When this basic idea of ​​the algorithm is a series of object called "GC Root" as a starting point, these nodes search from the beginning down the path to search through the chain of references is called when an object is not connected to the GC Roots any reference chain , then the object can not be used to prove this, the graph object object5, object6, object7 although Analyzing each other, but they are not to GC Roots reached, will be determined that they are recyclable objects

In the Java language, as GC Roots objects include the following:

  • 1, the virtual machine stack (local variables in the stack frame of the table) the object reference
  • 2, the method area static property class object reference
  • 3. The method of constant region of an object reference
  • 4, native method stacks of objects referenced JNI

Three, finalize () method ultimately determine whether the object is alive

Even if the object of analysis algorithms unreachable up in, it is not "Feisibuke", and this time they are temporarily in a "probation" stage, to really proclaim the death of an object, at least twice to go through the labeling process. Premise is marked When an object during reachability analysis found no references in connection with the GC Roots linked to each other.

1, marked the first time and the first screening

Screening condition is whether this object it is necessary to perform the finalize () method. When the object does not cover the finalize () method. Or finalize method has been invoked over the virtual machine, the virtual machine these two cases are considered "not necessary to perform" the object is recovered

2, the second mark

If the object is determined to be necessary to perform a finalize () method, then the object will be placed in a queue called the F-Queue, and later went by the virtual machine to automatically establish a low-priority thread Finalizer execute it. here's the so-called "execution" is a virtual opportunity to trigger this method, but does not promise it will wait for the end of the run, the reason for this, if an object in slow implementation finalize () method, or the occurrence of death cycle, will likely result in F-queue queue other objects permanently in wait, and even cause the entire memory recovery system crashes .finalize () method is the last chance to escape death fate of the object, later GC will be F-queue objects in a second small mark, if the object to be successful in their own rescue finalize () --- as long as the re-establishment associated with any object on the chain of references either. for example themselves (this keyword) assigned to a variable or class member variables of an object, then it will be removed collection "that the recovery," the second time mark; if the object has not escaped this time, it really is that essentially recycled A.

Flowchart is as follows:

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive(){
        System.out.println("yes,i am still alive:");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws InterruptedException {
        SAVE_HOOK = new FinalizeEscapeGC();
        //对象第一次成功解救自己
        SAVE_HOOK = null;
        System.gc();
        //因为finalize方法优先级很低,所以暂停0.5秒以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else {
            System.out.println("no,i am dead:(");
        }

        //下面这段代码与上面的完全的相同,但是这次自救却失败了
        SAVE_HOOK = null;
        System.gc();
        //因为finalize方法优先级很低,所以暂停0.5秒以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null){
            SAVE_HOOK.isAlive();
        }else {
            System.out.println("no,i am dead:)");
        }
    }
}
复制代码

operation result:

finalize method executed!
yes,i am still alive:
no,i am dead:)
复制代码

As seen from the results of operation, finalize SAVE_HOOK object () method does GC collector is triggered, and successfully escaped before being collected. Code has two identical code fragment, the result is the implementation of a successful escape, a failure, because finalize any object () method will only be called automatically once the system, if the object is facing a recovery at its finalize () method will not be executed again, and therefore the second paragraph of self-help behavior code It failed .finzlize () can do all the work, using a try-finally or otherwise can do better and more timely. but suggest that you completely forget about the existence of java language of this method.

Reproduced in: https: //juejin.im/post/5d08bc84f265da1b8b2b60da

Guess you like

Origin blog.csdn.net/weixin_34375233/article/details/93166278