虚拟机学习四-垃圾回收机制
重点关注几个问题:
-对象已死吗?
采用引用计数法(不能解决相互循环引用问题)或可达性分析法。
引用计数法:计算对象的引用数,计数为0时对象被回收。
可达性分析:通过一系列的"GC Roots"的对象(如栈中引用的对象、方法区中类静态属性引用的对象、常量引用的对象)作为起始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(不可达),则这些对象时不可用的。
-方法区中的回收
主要针对废弃的"常量"(程序中没有对象叫做“ab”,那常量池中"ab"将会被回收)和无用的类进行回收。
所谓无用的类,必须满足以下条件:
1、堆中不存在该类的任何实例2、加载该类的类加载器已经被回收3、该类对应的Class对象没有在任何地方被引用,即无法再任何地方通过反射访问该类的方法。
在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成jsp这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载功能以保证不会发生内存溢出。(至于类是否会被虚拟机回收,需要设置虚拟机参数-Xnoclassgc,以上三点只是必要条件)
-收集算法
标记-清除算法:
首先标记出需要回收的对象,在标记完成后统一回收所有被标记的对形象。但效率低,且清理后产生过多碎片。
复制算法:
将可用的内存容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。但这样空间代价太大了。所以有的实现是,并不按1:1划分,而是将内存分为一块较大的Eden空间和两块较小的Survior空间,每次使用Eden和其中一块Survivor。最后回收时,依然存活的对象便移动到另一块Survivor中。如果不够,则需要依赖其他内存(老年代)进行分配担保。
标记-整理算法:
与标记-清除算法相似,而是先整理后清除,即让所有还存活的对象都向一端移动后,然后直接清理掉端边界以外的内存。
分代收集算法:
这种算法没什么新的思想,只是根据对象存活的周期不同将内存或分为几块。一般是把Java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。在新生代中,每次都有大批对象死去,只有少量存活,那就用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而在老年代中因为对象存活率高且再没有额外空间对它进行分配担保,就必须用标记-清理\整理算法进行回收。
-垃圾收集器
serial收集器:
最基本、发展历史最悠久的收集器。这是一个单线程的收集器,工作时必须"Stop the world"即停止其他所有工作。虽然如此,它现在依然是虚拟机运行在client模式下的默认新生代收集器。因为其简单高效。
parNew收集器:
ParNew是Serial的多线程版本。除了多线程收集外,其他与Serial收集器相比没有太多创新之外,但却是许多运行在Server模式下的虚拟机中首选的新生代收集器。因为目前只有它能与CMS收集器(老年代收集器)配合工作。
Parallel Scavenge收集器:
与ParNew相似,它属于新生代收集器,也是多线程工作。但它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能第地缩短垃圾收集时用户线程的停顿时间。而Paraller Scavenge收集器的目标则是达到一个可控制的吞吐量。(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。前者适合用户交互多的任务,后者适合后台运算而不需要太多交互的任务。
Serial Old收集器:
Serial Old是Serial收集器的老年代版本,同样是单线程的收集器。主要给Client模式下的虚拟机使用。如果在Server模式下,它用于与Paraller Scavenge收集器搭配使用,另外作为CMS收集器的后备方案。
Parallel Old收集器:
是Paraller Scavenge收集器的老年代版本。在注重吞吐量以及CPU资源敏感的场合,可以优先考虑Parallel Scavenge加Parallel Old的组合。
CMS收集器:
CMS收集器是一种以获取最短回收停顿时间为目标的收集器。如现在很大一部分的Java应用集中在互联网网站或B/S系统的服务端上。
G1收集器:
当今收集器技术发展的最前沿成果之一。也是追求低停顿收集。