JVM 垃圾回收 -- 是否回收

95 阅读4分钟

1. 引用计数

当有地方引用引用值 + 1, 引用失效引用值 - 1. 为 0 回收

好处: 简单, 高效

坏处: 不适合所有场景, 如循环引用. 即使两个都已不在需要, 但引用值也无法减到0了.

2. 可达性分析

通过从 GC root, 来一个遍历. 能走到的说明有用, 走不到的说明无用了.

GC root可以为:

  1. ·在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。
  2. 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  3. 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
  4. 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  5. Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
  6. 所有被同步锁(synchronized关键字)持有的对象。
  7. 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

除了这几个基本的, 不同的垃圾回收器, 还有其他不同的对象加入

3. 再谈引用

何为引用, 一个属性存的为另一块内存的起始位置, 那么该属性就是某块内存, 某个对象的引用. 不过这样定义就很狭隘, 只能表示有引用, 或者无引用

在JDK 1.2版之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Re-ference)、软 引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)4种,这4种引用强 度依次逐渐减弱。

  • 强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回 收掉被引用的对象。
  • 软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内 存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存, 才会抛出内存溢出异常。在JDK 1.2版之后提供了SoftReference类来实现软引用。
  • 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只 能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只 被弱引用关联的对象。在JDK 1.2版之后提供了WeakReference类来实现弱引用。
  • 虚引用也称为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的 存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚 引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2版之后提供 了PhantomReference类来实现虚引用。

4. 生存还是死亡

在可能性分析后, 如果是不可达对象, 先进行标记, 随后进行筛选, 判断是否有必要执行 finalize() 方法. 不执行的条件, 1. 执行过了 2. 没有覆重写该方法. 然后将有必要执行的对象放入一个F-Queue的队列中, 由Finalizer线程去执行它们的finalize() 方法. 只管调用, 不等到执行结束, 因为这些如果产生死循环或睡眠啥的, 那么垃圾回收就停掉了.

如何自救, 比如在 finalize 方法中, 将自己的引用 this 赋值 给其他可达对象里, 下次能从 GC root 走到.

5. 方法区是否要回收

方法区, 在java虚拟机规范中说明, 可以不要求方法区进行垃圾回收.

主要回收该区域的, 废弃常量, 以及不用的类型

像第三方框架中, 反射,动态代理、CGLib等字节码框架. 动态生成大量自定义类型. 所以当有大量这种情况还是很有必要回收这一部分垃圾