转载

GC与内存分配策略

垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。

判断一个对象是否可被回收

1、引用计数算法创建一个引用计数器,当对象增加一个引用计数器就加1,引用失效时计数器减1,。引用计数器为0的对象可被回收。 在两个对象出现循环引用的情况下,此时引用计数器永远不为0,导致无法对他们进行回收。正式因为循环引用的存在,java虚拟机不使用引用计数算法。 2、可达性分析算法 以GC Roots 为起始点开始向下搜索,当一个对象到GC Roots没有任何引用链相连时则证明此对象是不可用的,将会被判定为可回收的对象。

Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 本地方法栈中 JNI(native方法) 中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中的常量引用的对象

3、方法区的垃圾回收因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高。 主要是对常量池的回收和对类的卸载。 为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。 类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载:

  • 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。

4、finalize即使是可达分型分析算法中不可达的对象也不是非死不可的,要宣告一个对象的死亡至少要经过两次标记的过程。

  1. GC进行第一次标记并进行一次筛选(筛选那些覆盖了finalize方法并且finalize方法是第一次调用的对象);
  2. 另一个低优先级的Finalizer线程去调用那些被筛选出来的对象的finalize方法;
  3. GC进行第二次标记,如果在前一步中那些筛选出来的对象没有在finalize拯救自己,此时,那些未被筛选到的和这些这些筛选到的但是没有拯救自己的对象都将会回收。

引用类型

1. 强引用被强引用关联的对象不会被回收。使用 new 一个新对象的方式来创建强引用。

Object obj = new Object();
复制代码

2. 软引用被软引用关联的对象只有在内存不够的情况下才会被回收,通常用在对内存敏感的程序中。使用 SoftReference 类来创建软引用。

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;  // 使对象只被软引用关联
复制代码

3. 弱引用被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。使用 WeakReference 类来创建弱引用。

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
复制代码

4. 虚引用又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知。使用 PhantomReference 来创建虚引用。

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, null);
obj = null;
复制代码
原文  https://juejin.im/post/5e762b61518825490b64a4b6
正文到此结束
Loading...