- Java中的引用:
-
- 强引用:
Object o=new Object(),只要引用还在不进行回收
-
- 软引用:
内存溢出之前,进行第二次回收会回收这部分对象,内存还不足,会抛出异常, SoftReference。
-
- 弱引用:
弱于软引用,下一次垃圾回收会回收该类型的对象。WeakReference
-
- 虚引用:
最弱的一种, 又称幽灵或幻影引用,不影响对象的生存,但是被引用对象被回收时会有通知,PhantomReference。
- 如何判断对象已死?
- 引用计数法,(JVM未使用,不能屏蔽相互引用的方式)
- 可达性分析,以一系列“GC Root”作为起点,找到不连通的节点判断为对象已死。
- 最后一次存活的机会finalize
-
- 对象被标记一次,后需要判断是否需要执行finalize方法。
-
- 若对象未覆盖finalize方法,对应已执行过finalize方法,则不执行finalize。
否则需要执行,对象会放到F-Queue队列中,有虚拟机创建的Finalizer(低优先级)
线程触发对象的finalize方法。不承诺会等待他结束。防止系统卡死回收时造成系统崩溃。
-
- 不建议覆盖finalize方法。
- 永久代的垃圾回收
-
- 方法区回收的性价比低
-
- 永久代垃圾回收主要两部分:废弃常量和无用类
-
- 判断一个无用类的条件如下:
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的实例
- 该类的ClassLoader已被回收
- 该类对应的java.lang.Class没有任何地方被引用。即没有任何地方通过范式访问该类。
-
-
- 相关参数:
-
-Xnoclassgc,关闭类回收
-verbose:class (显示类详细信息)和
-XX+TraceClassLoading(跟踪类加载信息) 需要Product版本JVM
-XX+TraceClassUnLoading(跟踪类卸载信息) 需要FastDebug版本JVM
-
- 大量使用反射,动态代理,CGLib的框架需要虚拟机开启类卸载功能,防止永久待溢出。
-
垃圾回收算法
-
- 标记-清除算法,没有使用,缺点:大对象无法找到连续的内存,触发GC
-
- 复制算法:新生代标准算法,Eden:Survivor=8:1 ,1块Eden(80%)和2块Surivor(from(10%),to(10%)),预计只有10%作为预留空间。
回收时,将存活对象拷贝到预留的Survivor中,之后清除已使用的Eden和Survivor。
预留Surivor不够时,需要老年代进行分配担保。
-
- 标记整理:标记后,将所有存活对象向一端移动,之后清除边界外的对象。(老年代算法)
-
- 分代算法,整合上述算法,将JVM分成,新生代和老年代,新生代使用复制算法,老年代使用标记-清除 或标记整理算法。
- HotSpot算法实现细节
-
- 枚举根节点
- 必须暂停所有线程(Stop the world),目前JVM都是准确式GC,即可以准确知道那些地方存放着对象的引用。
- HotSpot通过OopMap实现。类加载完成把类内部什么偏移量是什么类型数据计算出来,
在JIT编译过程中会在特定的位置记录下栈和寄存器中那些位置的引用。
-
- 安全点(解决程序执行时进入GC的问题)
造成OopMap的内容变化的指令非常多,JVM并未对每条指令都记录OopMap,以避免过多的内存占用。
JVM只在“特定的位置”记录OopMap,这些位置被称为安全点。只有到达安全点才能暂停开始GC。
安全点的选择以“是否让程序长时间执行的特征”为标准进行选定。
(最明显的是指令复用)例如:方法调用,循环跳转,异常跳转等。
-
-
- 线程如何到达安全点,抢占式,主动式。
-
- 抢占式,没有JVM使用,在GC发生时,首先中断所有线程,若有线程未到安全点,就恢复线程,让“它”跑到安全点。
- 主动式,简单的设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标示为真时,线程自己中断挂起,
轮询的地方和安全点重合,外加上创建对象需要分配内存的地方。
-
- 安全区域 (解决 程序“不执行”时进入GC,例如休眠,阻塞)
一段代码区域中,引用关系不会发生变化。可以把安全区域看做是安全点的扩展。
线程执行到安全区域时,标示自己进入安全区,发生GC时,不用管在安全区域中的线程。
线程若离开安全区域,检查是否已完成了根节点枚举,若完成了线程继续执行,否则线程必须等待,
直到收到可以离开安全区的信号为止。