JVM学习8·判断对象的存活
当一个对象没有被任何对象引用,就证明这个对象没有用,那么成为了被回收对象了。
判断对象是否是垃圾的方法有两种:引用计数法、可达性分析
1.对象回收
1.1引用计数法算法
引用计数法就如图中所述,如果有对象引用了对象,则这个对象的引用计数为1,如果没有则为0,但是有一个问题就是,当两个对象互相引用时候,而没有别的对象引用他们,是不是就证明这两个对象其实都是无用的呢?
1.2可达性分析算法(根可达)
来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为 引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。
作为 GC Roots 的对象包括下面几种(重点是前面 4 种):
- 虚拟机栈(栈帧中的本地变量表)中引用的对象;各个现场被调用方法堆栈中使用到的参数、局部变量、临时变量等。
- 方法区中类静态属性引用的对象;java类的引用类型静态变量。
- 方法区中常量引用的对象;比如:字符串常量池里的引用。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。
- JVM的内部引用(class对象、异常对象NullPointException、OutofMemoryError,系统类加载器)。(非重点)
- 所有被同步锁(synchronized关键)持有的对象。(非重点)
- JVM内部的JMXBean、JVMTI中注册的回调、本地代码缓存等(非重点)
- JVM实现中的“临时性”对象,跨代引用的对象(在使用分代模型回收只回收部分代的对象,这个后续会细讲,先大致了解概念)(非重点)
2.Class回收
Class 要被回收,条件比较苛刻,必须同时满足以下的条件(仅仅是可以,不代表必然,因为还有一些参数可以进行控制):
- 1、该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例。
- 2、 加载该类的 ClassLoader 已经被回收。
- 3、 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
-
4、 参数控制:
废弃的常量和静态变量的回收其实就和 Class 回收的条件差不多。
3.Finalize 方法
即使通过可达性分析判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是 没有找到与 GCRoots 的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了 finalize),我们可以在 finalize 中去拯救。
4.引用关系
4.1强引用
一般的 Object obj = new Object() ,就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象,即便是JVM报错OOM也不会回收强引用的数据。
4.2软引用
一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(OuyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的 空间,才会抛出内存溢出)。参见代码:
VM 参数 -Xms10m -Xmx10m -XX:+PrintGC
可以看到这个软引用即使执行垃圾回收,或者这个类是有一个实例,也就是只有一个软引用,然后进行一次gc也不会被垃圾回收,只有当内存不够,即将报错OOM的时候才会把软引用的数据给垃圾回收掉。
例如,一个程序用来处理用户提供的图片。如果将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费 内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都 要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。
4.3 弱引用
弱引用比软引用更加脆弱,只要发生垃圾回收就会被回收掉。
4.4 虚引用
幽灵引用,最弱(随时会被回收掉) 垃圾回收的时候收到一个通知,就是为了监控垃圾回收器是否正常工作。