第三章 垃圾收集器与内存分配策略
目前内存的动态分配与内存回收技术已经相当成熟和自动化,为什么还需要去了解GC和内存分配?:当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的计划实施必要的监控和调节;
第二章介绍了Java内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭;这几个区域的内存分配和回收都具备确定性,随着方法结束或者线程结束时,内存自然就跟随着回收了;Java堆 和 方法区则不一样,这部分内存的分配和回收都是动态的;
引用计数法:无法解决循环引用问题; 可达性分析法:通过一系列称为GC Roots的对象作为起始点,向下搜索形成引用链,当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可用的; 在Java语言中,可以作为GC Roots的对象有下面几种: 1. 虚拟机栈(栈帧中的本地变量表)中引用的对象 2. 方法区中类静态属性引用的对象 3. 方法区中常量引用的对象 4. 本地方法栈中JNI(即一般说的Native方法)所引用的对象
强引用(只要存在强引用,垃圾收集器永远不会回收掉被引用的对象)、软引用(对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常)、弱引用(被弱引用关联的对象只能生存到下一次垃圾收集发生之前)、虚引用; 两次标记过程:
回收方法区(部分虚拟机的永久代):回收 废弃常量 和 无用的类; 类需要满足下面3个条件才能算是“无用的类”: 1. Java堆中不存在该类的任何实例 2. 加载该类的ClassLoader已经被回收 3. 该类对应的java.lang.Class 对象没有在任何地方被引用, 无法在任何地方通过反射访问该类的方法; 类并不是和对象一样,不使用了就必然会回收。 在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出;
垃圾收集算法: 标记-清除算法: 先标记,后清除;效率不高,会产生内存碎片; 复制算法: 将内存按容量划分为大小相等的两块,每次使用其中一块,当一块内存用完,就将还存活着的对象复制到另一块上面,然后再把已经使用过的内存空间一次清理掉;缺点:将内存缩小为原来一半,代价太高; 现代商业虚拟机都采用复制算法来回收新生代; Eden 、From Survivor 、To Suvivor(8:1:1); 标记-整理算法: 对象存活率较高的时候复制收集算法要执行较多的复制操作,效率较低,还需要额外的空间进行分配担保;所以在老年代使用“标记-整理算法”,先标记,再把存活的对象都向一段移动,再直接清理掉边界以外的内存; 分代收集算法: 在新生代中,每次垃圾收集都发现有大批对象死去,少量存活,选用复制算法;老年代对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记-清除”或“标记-整理”算法来进行回收;
GC停顿:
垃圾收集器长时间停顿,表现在 Web 页面上可能是页面响应码 500 之类的服务器错误问题,如果是个支付过程可能会导致支付失败,将造成公司的直接经济损失。两个原因:
- 在 CMS 启动过程中,新生代提升速度过快,老年代收集速度赶不上新生代提升速度
- 在 CMS 启动过程中,老年代碎片化严重,无法容纳新生代提升上来的大对象
发送这种情况,应用线程将会全部停止(相当于网站这段时间无法响应用户请求),进行压缩式垃圾收集(回退到 Serial Old 算法)
新生代提升过快问题:(1)如果频率太快的话,说明空间不足,首先可以尝试调大新生代空间和晋升阈值。(2)如果内存有限,可以设置 CMS 垃圾收集在老年代占比达到多少时启动来减少问题发生频率(越早启动问题发生频率越低,但是会降低吞吐量,具体得多调整几次找到平衡点),参数如下:如果没有第二个参数,会随着 JVM 动态调节 CMS 启动时间
-XX:CMSInitiatingOccupancyFraction=68 (默认是 68)
-XX:+UseCMSInitiatingOccupancyOnly
老年代碎片严重问题:(1)如果频率太快或者 Full GC 后空间释放不多的话,说明空间不足,首先可以尝试调大老年代空间(2)如果内存不足,可以设置进行 n 次 CMS 后进行一次压缩式 Full GC,参数如下:
-XX:+UseCMSCompactAtFullCollection:允许在 Full GC 时,启用压缩式 GC
-XX:CMSFullGCBeforeCompaction=n 在进行 n 次,CMS 后,进行一次压缩的 Full GC,用以减少 CMS 产生的碎片
安全点: