如何判断对象是否可回收
引用计数法
1、概念:给对象中添加一个引用计数器,每当有一个地方引用他时,计数器的值+1,当引用失效的时候,计数器-1,任何时刻计数器为0的对象就是不可以在被使用的对象。
2、缺点:无法解决对象循环引用的问题(如下图)
可达性分析法
1、概念:垃圾回收根节点(GCRoot)向下搜索,搜索所走过的路径称为引用链,当一个对象对GCRoot没有任何的引用链时,代表当前对象不可用。
2、GCRoot包含的对象:
- 虚拟机栈(帧栈中的本地变量表)中的引用的对象
- 方法区类静态属性 所引用的对象
- 方法区常量所引用的对象
- 本地方法栈中锁引用的对象
3、引用的概念
- 强引用:垃圾收集器永远不会回收掉被引用的对象;Object obj = new Object();
- 软引用:系统将要发生内存溢出之前,会把这类对象归类到回收范围中并进行第二次回收;SoftReference
- 弱引用:强度比软引用更弱,被弱引用关联的对象只能生存到下一次垃圾收集发生之前;WeakReference
- 虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象实例,为一个对象设置虚引用关联的目的就是系统能在这个对象被收集器回收时得到一个系统通知;PhantomReference
回收算法
标记清除算法
1、概念:标记出所有需要回收的对象,在标记完成后统一回收掉被标记的对象。
2、缺点:
- 效率问题:标记和清理的效率不高
- 空间问题:会产生大量的不连续的内存碎片,空间碎片太多可能会导致在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作
复制算法
1、概念:将可用的内存按容量分为大小相等的两块,每次只使用其中的一块,当这一块内存用完时,将存活的对象复制到另外一个上面,然后将已经使用过的内存空间一次清理掉;
2、优点:解决标记清除算法中存在的内存碎片问题
3、缺点:
- 空间浪费:将内存缩小为原来的一半,内存使用效率低。
- 对象存活率较高时就要执行多次的复制算法,效率变低。
- 只能用于新生代,无法用户老年代这种对象存活率较高的情况
3、复制算法优化:
用该算法回收新生代,将内存分为一块较大的Eden和两块Survivor空间(HotSpot默认比例是8:1),每次使用Eden和其中的一块Survivor空间,当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另外一个Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间,这样只会浪费10%的空间;如果另外一个Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。
标记整理算法
1、概念
过程与标记清除算法一致,但后续在清理的时候不是直接对可回收的对象进行清理,而是让所有存活的对象都向另外一端移动,然后直接清理掉端边界以外的内存
2、优点:老年代可以用,可以解决标记复制算法在对象存活较高时效率低下的问题
分代收集算法
1、概念:根据对象的存活周期将内存划分为几块,JAVA堆分为新生代和老年代,根据各个年代的特点采用适当的收集算法,新生代收集时大量对象被收集,小亮对象存活,所有采用复制算法,老年代存活率高,没有额外的空间对它进行分配担保,可以用标记清理或者标记整理算法。
垃圾回收器
Serial
1、概念:最基本收集器,新生代收集的唯一选择,单线程收集器
2、优点:相对于其他收集器的单线程比,没有线程交互的开销,简单切高校
3、缺点:在进行垃圾收集时,会暂停其他的工作线程(Stop The World)
Parnew
1、概念:Serial收集器的多线程版本
2、优点:除了Serial收集器外,只有它能与CMS收集器配合使用
3、缺点:还是存在Stop The World的问题,只不过比Serial稍微好点
4、相关参数:
- -XX:+UseParNewGc 使用parnew收集器
- -UseConcMarkSweepGc 使用该选项时,会默认将该收集器做为新生代的收集器
- 并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
- 并发:用户线程和垃圾收集线程同时执行;
Parallel Scavenge 收集器
1、概念:新生代收集器,使用复制算法的并行多线程收集器;目标是达到一个可控制的吞吐量,适用于后台运算不需要太多交互的任务;该收集器可以与Serial Old配合使用
2、缺点:无法和CMS收集器一起使用
3、相关参数:
- -XX:MaxGcPauseMillis 控制最大垃圾收集停顿时间(大于0的毫秒数),该值越小吞吐量越低,相对的
- -XX:GCTimeRatio 吞吐量大小 参数值(垃圾收集时间占总时间的比率)大于0小与100的整数,正常是吞吐量的倒数
- -XX:+UseAdaptiveSizePolicy 打开后,不需要手动指定新生代的大小(-Xmn)、Eden和Survivor的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等参数,虚拟机回根据当前系统的运行情况收集戏能健康信息,动态调整来提供合适的停顿时间或者是吞吐量
吞吐量:CPU用户运行代码的时间与CPU总消耗时间的比值,吞吐量=运行代码时间/(运行用户代码时间+垃圾收集时间)
Serial Old 收集器
1、概念:Serial收集器的老年代版本,单线程,采用标记整理算法
Parallel Old收集器
1、概念:Parallel Scavenge收集器的老年代版本,多线程,采用标记整理算法;在注重吞吐量和CPU资源敏感的情况下,可以与Parallel Scavenge 配合使用
CMS(Concurrent Mark Sweep)收集器
1、概念:一种以获取最短回收停顿时间为目标的收集器,基于标记清除算法实现的;默认开启回收线程数是(CPU+3)/4
2、运作过程:
- 初始标记:
- 并发标记:
- 重新标记:
- 并发清除:
3、优点:
- 并发收集
- 低停顿
4、缺点:
- 对CPU资源敏感:虽然不会Stop the world,但是会占用部分线程导致用户程序变慢,吞吐量变低。当CPU在四个以上时,收集线程占用不会超过25%,低于4个时,影响就很大了;
- 优化方式:增量式并发收集器:i-CMS,在并发表基和并发清理的时候让GC线程和用户线程交替运行,减少GC线程独占资源的时间,这样可能会导致收集时间边长,但是对程序的影响较低
- 浮动垃圾:因为浮动垃圾的存在,可能出现Concurrent Mode Failure的情况。由于CMS的特性,在垃圾收集阶段用户线程继续运行,必须预留足够的内存空间给用户线程使用,所以CMS无法和其他收集器一样,等老年代完全满了才开始收集,需要预留一部分空间提供并发收集时的程序运作使用。默认情况下,老年代占用68%就会被激活收集;这个可以通过-XX:CMSInitiatingOccupancyFraction来做调整;
- 空间碎片问题:因为基于标记清除算法,所以会导致碎片的问题
- 优化方式:-XX:+UseCMSCompackAtFullCollection fullGC完以后自动进行碎片的压缩(导致停顿时间变长);-XX:CMSFullGCsBrforeCompaction 设置多少次不压缩的fullGC后,执行一次压缩的FullGC
- 并发清理的同时因为不Stop The world导致会同时产生新的垃圾,这部分垃圾叫做浮动垃圾;
- ConcurrentMode Failure:预留内存无法满足程序运行的需要时,会发生Concurrent Mode Failture,发生后会临时采用会临时启动Serial Old来进行收集,这会导致停顿时间更长
G1收集器
1、概念:基于标记整理的算法实现
2、优点:
- 不会产生碎片
- 可以精确控制停顿