垃圾回收机制具有以下特征
1.垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源。
2.程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候进行。当对象永久的失去引用后,系统会在合适的时候回收它所占d的内存。
3.在垃圾回收机制回收任何对象之前,总会调用它的finalize()方法,该方法可能使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消回收。
而对象在堆内存中运行时,根据它被引用变量所引用的状态,可以把它的状态分为三种:
1.可达状态:当一个对象有引用变量引用时。
2.可恢复状态:当某个对象不再有任何引用变量引用它时,它就进入了可恢复状态。在这种状态下,系统的垃圾回收机制准备回收该对象的内存,在回收之前,系统会调用所有可恢复状态对象的finalize()方法。如果在调用时让一个引用重新引用该对象,则该对象变为可达状态,否则为不可达状态。
3.不可达状态:当一个对象失去所有的引用变量,且系统调用finalize()方法后依然没有使对象变为可达状态,那么这个对象将永久的失去引用,最后变成不可达状态。然后系统就会真正回收该对象所占有的资源。
一个对象可以被一个方法的局部变量引用,也可以被其它类的类变量引用,而当这个类被销毁后,该对象才会进入可恢复状态。也可以被其它对象的实例变量引用,只有当那个实例变量的对象销毁后,这个对象才会进入可恢复状态。
强制系统垃圾回收有两个方法:
1.调用System类的gc()静态方法:System.gc();
2.调用Runtime对象gc()实例方法:Runtime.getRuntime().gc();
finalize()方法具有如下四个特点:
3.当jvm执行可恢复对象的finalize()方发送时,可能使该对象或其它对象变成为可达状态。
4.当jvm执行finalize()方法出现异常时,垃圾回收机制并不会报告,程序会继续执行。
对大部分对象来说,程序中都会有一个引用变量来引用该对象。java中对对象的引用有如下四种方式:
1.强引用:最常见的引用方式。创建一个对象并把对象赋给一个引用变量。它处于可达状态,不可能被垃圾回收机制回收。
2.软引用:它需要通过SoftReference类来实现。当一个对象只有软引用时,它有可能被垃圾回收机制回收。当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象。当系统内存不足时,系统可能会回收它。软引用通常用于对内存敏感的程序中。
3.弱引用:弱引用通过WeakReference类实现,软引用和弱引用很像,但是弱引用的引用级别更低。对于只有弱引用的对象而言,当垃圾回收机制运行时,不管系统的内存是否足够,总会回收该对象所占有的内存。
4.虚引用:虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列联合使用。
引用队列由java.lang.ref.Reference类表示,它用于保存被回收后对象的引用。当联合使用软引用,弱引用,引用队列时,系统在回收被引用的对象之后,将把被回收对象对应的引用添加到引用队列中,与软引用和弱引用不同的是,虚引用在对象在对象被释放之前,将把它对应的虚引用添加到它关联的引用队列中。
java是通过可达性分析算法来判定对象是否存活,这个算法的思路是通过一系列的称为“GC Roots"的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots 没有任何引用链相连,则证明这个对象是不可用的。在java语言中,可作为GC Roots的对象可包括以下几种:
虚拟机栈的本地变量表中引用的对象
方法区中类静态属性引用的对象
- 方法区中常量引用的对象
-
- 本地方法栈中JNI(即一般说的native方法)引用的对象
即使在可达性分析算法中不可达的对象,也并非是”非死不可“的,它们正在的被回收至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记,并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者虚拟机已经调用过finalize()方法,虚拟机将这两种情况视为没有必要执行。
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在 稍后由一个虚拟机自动建立的,低优先级的Finalizer线程去执行它。这里所谓的执行是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。因为如果 一个对象在finalize()方法中执行缓慢,更极端一点的是发生死循环,将会导致队列中的所有的对象都在等待它,甚至整个内存回收系统崩溃。之后虚拟机将对F-Queue队列进行第二次标记,如果对象在finalize()方法中只要重新与引用链上的任何一个对象建立关联即可。那么在第二次标记中它将被移除”即将回收"集合,没有没有被移除它就真的被回收了。
在java堆中的方法区即永久代的垃圾回收主要回收两部分的内容:废弃常量和无用的类。当一个常量没有任何对象引用时,它就是废弃常量。而一个类要满足三个条件才能算是无用的类:1.该类的所有实例都已经被回收,也就是java堆中不存在该类的任何实例。2.加载该类的ClassLoader已经被回收。3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class以及-XX:+TraceClassLoading(product版虚拟机), -XX:+TraceUnLoading(FastDebug版虚拟机) 查看类加载和卸载的信息。