关于jvm:JVM-垃圾回收概述

58次阅读

共计 4765 个字符,预计需要花费 12 分钟才能阅读完成。

我是清都山水郎,天教懒慢带疏狂。曾批给露支风券,累奏流云借月章。诗万首,酒千觞,几曾着眼看侯王。玉楼金阙慵归去,且插梅花醉洛阳。

在进行垃圾回收的时候,对于 JVM 而言,什么对象才算是垃圾呢?如何判断某些对象是垃圾呢?
很显著的,曾经没有被应用的对象,就是垃圾。

援用计数法

援用计数法是用于判断对象是垃圾的一种形式。
如果被其余对象援用,那么对象的援用计数就会 +1。当援用生效时,援用计数就 -1。当援用等于 0 时,即示意对象已无用。
援用计数尽管实现简略,然而 无奈解决循环援用的问题,也因而没有被采纳

循环援用

对象 A 援用对象 B,对象 B 的援用 +1;对象 B 援用对象 A,对象 A 的援用 +1。然而对象 A,对象 B 没有被应用。从上帝视角来看,A、B 曾经是无用对象了。然而因为援用计数不等于 0,因而不会被认为是垃圾。

GC Root 可达性剖析

JVM 应用 GC Root 可达性分析判断对象是否为垃圾。
对象是否应该被回收,判断条件如下

  1. Root 对象登程,如果对象可被拜访到,那么此对象就不应该被回收。
  2. 对象被回收前,可执行一次 finalize() 办法,如果在 finalize() 办法中复活,则不会被回收。须要留神的是:finalize() 办法只会被 一个对象执行一次

根对象定义

想必,你肯定很纳闷,what is root object?
官网文档定义:

Garbage Collection Roots
A garbage collection root is an object that is accessible from outside the heap. The following reasons make an object a GC root:

  1. System Class

Class loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* .

  1. JNI Local

Local variable in native code, such as user defined JNI code or JVM internal code.

  1. JNI Global

Global variable in native code, such as user defined JNI code or JVM internal code.

  1. Thread Block

Object referred to from a currently active thread block.

  1. Thread

A started, but not stopped, thread.

  1. Busy Monitor

Everything that has called wait() or notify() or that is synchronized. For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means >object.

  1. Java Local

Local variable. For example, input parameters or locally created objects of methods that are still in the stack of a thread.

  1. Native Stack

In or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.

  1. Finalizable

An object which is in a queue awaiting its finalizer to be run.

  1. Unfinalized

An object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.

  1. Unreachable

An object which is unreachable from any other root, but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.

  1. Java Stack Frame

A Java stack frame, holding local variables. Only generated when the dump is parsed with the preference set to treat Java stack frames as objects.

  1. Unknown

An object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. For these dumps the MAT parser marks objects which are have no inbound references or are unreachable from any other root as roots of this type. This ensures that MAT retains all the objects in the dump.

其余参考:知乎答复

通过 finalize() 复活

public class FinalizeTest {

    public static FinalizeTest obj;

    public static void main(String[] args) throws InterruptedException {obj = new FinalizeTest();
        obj = null;
        System.gc();
        Thread.sleep(1000);
        if (obj == null) {System.out.println("obj = null");
        } else {System.out.println("obj 可用");
        }
        System.out.println("第 2 次 GC");
        obj = null;
        System.gc();
        Thread.sleep(1000);
        if (obj == null) {System.out.println("obj = null");
        } else {System.out.println("obj 可用");
        }
    }

    @Override
    protected void finalize() throws Throwable {super.finalize();
        System.out.println("对象复活");
        obj = this;
    }
}

以上程序顺次打印如下:

对象复活
obj 可用
第 2 次 GC
obj = null

finalize() 对于一个对象而言,只会在要被回收时,才会调用一次。在第 1 次 GC 时,通过 finalize 复活,第二次 GC 没有调用 finalize,顺利被回收。

蹩脚的 finalize

finalize() 办法由 FinalizerThread 线程执行。一个对象若重写 finalize() 办法,那么在被回收时,会被退出到 FinalizerThread 的执行队列 FinalReference 中。
此时对象会被 Reference 中的 referent 字段所指向,因而对象会从新变成可达。如果 finalize() 过于耗时,那么会妨碍 GC 的失常回收。
上面就展现一段蹩脚的 finalize 实现。
应用 -Xms10m -Xmx10m 执行 一段时间后,会报 OOM

public class FinalizeReferenceTest {public static void main(String[] args) {for (int i = 0; i < 50000; i++) {LF lf = new LF();
        }
    }

    public static class LF {private byte[] bs = new byte[1024];

        @Override
        protected void finalize() throws Throwable {super.finalize();
            try {Thread.sleep(1000);

            } catch (Exception e) {}}

    }
}

finalize()的实现中,Thread.sleep(1000) 示意在执行耗时的工作。FinalizerThread 在执行 finalize() 办法时,是从队列中一项一项取出来执行的, finalize() 执行过久,导致大量对象始终被援用指向,无奈失常回收。

如果将 LE#finalize() 办法去掉,程序会失常执行。

援用队列

JAVA 中依据对象援用强弱,有 4 大援用对象:强援用,软援用(SoftReference),弱援用(WeakReference),虚援用(PhantomReference)。
失常创立的对象为强援用对象。对于 软援用,弱援用,虚援用,当对象被回收时,会被退出到援用队列中,能够通过援用队列来监听到对象被回收。

上面的 demo 展现了,当对象被回收后,通过援用队列进行监控

public class ReferenceQueueTest {static ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();

    public static void main(String[] args) throws InterruptedException {monitorSoftReference();
        addSoftReference();}

    private static void addSoftReference() throws InterruptedException {Object o = new Object();
        ObjectWeakReference reference = new ObjectWeakReference(o, referenceQueue);
        o = null;
        System.gc();
        Thread.sleep(1000);
    }

    private static void monitorSoftReference() throws InterruptedException {Thread thread = new Thread(() -> {while (true) {
                ObjectWeakReference reference = null;
                try {reference = (ObjectWeakReference) referenceQueue.remove();} catch (Exception e) {e.printStackTrace();
                }
                if (reference != null) {System.out.println("对象被回收");
                }
            }
        });

        thread.start();}

    public static class ObjectWeakReference extends WeakReference<Object> {public ObjectWeakReference(Object referent, ReferenceQueue<? super Object> q) {super(referent, q);
        }
    }
}

参考

Garbage Collection Roots
JAVA 垃圾回收,线程栈中哪些货色作为 GC RootS?
ReferenceQueue 的应用
《实战 Java 虚拟机:JVM 故障诊断与性能优化(第 2 版)》

既然抉择了远方,即便天寒地冻,路遥马亡,我本就赤贫如洗,又有何惧。

‘ 深刻交换 ’

正文完
 0