1.浅谈引用
引用分为四种:强引用,软引用,弱引用,虚引用。
这四种引用强度依次减弱。
强引用:类似于“Object obj=new Object()”这类引用,
只要强引用还存在,引用的对象就永远不会被回收。
软引用:描述一些还有用但并非必需的对象。
在将要发生内存溢出异常之前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
弱引用:描述非必需对象,强度比软引用更弱。
只被弱引用关联的对象在本次垃圾回收中一定会被回收。
虚引用:最弱的一种引用关系。
唯一目的就是在该对象被收集器回收时收到一个系统通知。
// 强引用
Reference ref = new Reference();
// 软引用 内存不足时回收弱引用
SoftReference softReference = new SoftReference<Reference>(ref);
// 弱引用 垃圾回收时必定回收此对象
WeakReference<Reference> weakReference = new WeakReference<Reference>(ref);
// 引用队列
ReferenceQueue<Reference> queue = new ReferenceQueue<>();
// 虚引用
PhantomReference<Reference> phantomReference = new PhantomReference<Reference>(ref,queue);
2.引用计数法
定义:
每个对象拥有一个计数器:
每当有一个引用指向它,引用计数器+1;
每当指向它的一个引用失效时,引用计数器-1;
特点:
实现简单,判定效率高。
但是很难解决对象之间循环引用的问题。
3.可达性分析算法(JVM)
定义:
通过一系列的名为"GC Roots"的对象作为起始点,
从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),
当一个对象到 GC Roots没有任何引用链相连,即从 GC Roots到这个对象不可达,则证明此对象是不可用的。
GC Roots 对象:
虚拟机栈(栈帧中的本地变量表)中引用的对象;
方法区中类静态属性实体引用的对象;
方法区中常量引用的对象;
本地方法栈(即一般说的Native方法)中JNI引用的对象。
4.生死之道
一个对象要宣布死亡,至少要经历两次标记过程。
任何一个对象的finalize()只会被执行一次。
1.可达性分析后发现此对象没有与 GC Roots 相连接的引用链。此时进行第一次标记并进行一次筛选:是否有必要执行 finallize()
没必要执行:
1.未覆盖 finallize();
2.finallize()已经被调用过;
有必要执行:
1.对象被放置在F-Quene队列中,虚拟机自动建立一个低优先级的Finalizer线程触发其finalize()。
2.finalize()是对象逃脱的最后一次机会。只要在finalize()中重新与引用链上的对象建立关联即可逃脱。
2.稍后GC对F-Quene队列中的对象进行第二次标记。
然后对象就会被真正的回收。
此处未明确说明没有筛选上的对象如何处理,博主认为在对二次标记对象回收时直接对未筛选的对象进行回收。
筛选只为给那些未执行finalize()的对象最后一次活命的机会。(像不像德莱文的故事?)
5.回收方法区
垃圾收集对象:废弃常量和无用的类
废弃常量:收集类似于java堆中的对象。
当常量没有任何引用指向它时,此时若发生内存回收,且有必要时。这个常量会被清理出常量池。
无用的类:
1.java堆中不存在该类的实例;
2.加载该类的 ClassLoder 已经被回收;
3.该类对应的 Clas 对象未在任何地方被引用,无法再任何地发通过反射获取该类信息。
虚拟机可以(不是必须,不一定会回收)回收无用类。
HotSpot虚拟机提供 -X:noclassgc参数进行控制;
频繁自定义 ClassLoder 的场景都需要具备卸载类的功能,以保证永久代不会溢出。