java虚拟机知识点-垃圾收集器与内存分配策略

一、概述
1、线程私有:程序计数器、虚拟机栈、本地方法栈
2、运行期间分配和回收内存:Java堆和方法区
二、对象存活判定算法
  1. 可达性分析算法
  1. 通过一系列成为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain);当对象不存在引用链时则为不可用
  2. 可作为“GC Roots”的对象:
  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中JNINative方法)应用的对象
  1. 对象的重要级别
  1. 强引用:该对象不会被回收
  2. 软引用:系统将要发生内存溢出异常之前会被列入回收范围之中,第二次gc回收
  3. 弱引用:其对象只能生存到下一次回收之前
  4. 虚引用:唯一目的是被该对象回收之时收到一个系统通知
  1. 要回收一个对象,至少要经历两次标记:不可达标记并进行一次筛选
  1. 对应的类没有覆盖finalize方法或其finalize已被虚拟机调用过:认为该对象没必要执行
  2. 有必要执行finalize方法:该对象被加入F-Queue队列中,由Finalizer线程负责执行
  3. 任一对象的finalize方法都只会被系统自动调用一次
  1. 回收方法区
  1. 判断为可(仅仅是可以)回收的类
  1. Java堆不存在该类的实例
  2. 加载该类的ClassLoader已经被回收
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,没有通过反射访问该类的方法
  1. 废弃常量:系统中没有该常量字面量
三、垃圾收集算法
  1. 标记-清理算法
  1. 标记所有需要回收的对象
  2. 标记完成后统一回收
  3. 缺点:效率不高,标记清除之后产生大量不连续的内存碎片
  1. 复制
  1. 将可用内存划分为大小相等的两块,每次只用一块
  2. 缺点:内存利用率低
  3. 改进:分为新生代对象和来年代对象,分配一块Eden80%)和两块Survivor空间
  1. 标记-整理
  1. 标记
  2. 让所有存活的对象都向一端移动,然后直接清理端边界以外的内存
  1. 分代收集算法
  1. 根据对象存活周期的不同将内存划分为几块
  2. 新生代:复制算法
  3. 老年代:标记-清理或标记-整理
四、HotSpot的算法实现
  1. 枚举根节点
  1. 枚举根节点(GC Roots)时必须要停顿
  2. OopMap的数据结构:记录对象的数据类型,引用所在栈和寄存器中的位置
  1. 安全点(Safepoint
  1. 让所有线程(不包括执行JNI调用的线程)都到最近的安全点上再停顿下来
  1. 抢先式中断:没到安全点的线程都让它跑到安全点
  2. 主动式中断:设置一个标志,线程执行时发现该标志,并为真时就自己中断挂起
  1. 安全区域(Safe Region):在一段代码片段之中,引用关系不会发生变化。在这个区域中的任意地方开始GC都是安全的
  1. JVM发出GC,不用管在安全区域的线程;该线程要离开该Safe Region前检查是否完成了根节点枚举,若没有,则等待离开信号
4JVM不是为每条指令都生成OopMap,只有安全点才记录
五、垃圾收集器
  1. 垃圾收集器:两个收集器之间存在连线,就说明他们可搭配使用(没有最好的收集器
java虚拟机知识点-垃圾收集器与内存分配策略
  1. Serial收集器
  1. GC时,必须暂停其他所有的工作线程
  2. Client模式下的默认新生代收集器;单线程高效
  1. ParNew收集器:“并行”
  1. Server模式下的虚拟机中首选的新生代收集器;
  2. 多线程收集;默认开启的收集线程数与CPU的数量相同
  1. Parallel Scavenge收集器
  1. 新生代收集器,采用复制算法
  2. 目标:控制的吞吐量
  1. 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
  1. 设置参数
  • 最大垃圾收集停顿时间:-XX:MaxGCPauseMillis
  • 吞吐量大小:-XX:GCTimeTatio
  • –XX:+UseAdaptiveSizePolicy:自适应配置参数的开关
  1. Serial Old收集器:Serial的老版本
  1. 标记-整理
  2. 可与Parallel Sanvenge收集器搭配
  3. 作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用
  1. Parallel Old收集器
  1. 标记-整理算法
  2. 适用于注重吞吐量和CPU资源敏感的场合
  1. CMSConcurrent Mark Sweep)收集器
  1. 目标:最短回收停顿时间
  2. 基于标记-清除算法
  1. 初始化标记:“Stop The World”;标记GC Roots能直接关联到的对象
  2. 并发标记:进行GC Roots Tracing的过程
  3. 重新标记:标记并发标记期间的标记发生变动的对象
  4. 并发清除
  1. 缺点
  1. CMS收集器对CPU资源非常敏感(占用CPU资源)
  1. CMS默认启动的回收线程数为(CPU数量+3/4
  2. 增量式并发收集器(Incremental Concurrent Mark Sweep/i-CMS):GC线程、用户线程交替运行
  1. CMS收集器无法处理浮动垃圾(Floating Garbage
  1. 浮动垃圾:GC本次不能清理的产生的新垃圾
  1. 产生有大量空间碎片
  1. –XX:+UseCMSCompactAtFullCollection开关参数:内存碎片整理
  2. –XX:CMSFullGCsBeforeCompaction:执行多少次不压缩的Full GC后,跟着来一次带压缩的;默认值为0
  1. G1Garbage-First)收集器
  1. 面向服务端应用
  2. 并发:充分利用多CPU、多核环境下的硬件优势
  3. 分代收集:将对象分为老年代、新年代
  4. 空间整合:外部-“标记-整理,内部-“复制”;不会产生内存空间碎片
  5. 可预测的停顿:除了追求低停顿外,还能建立可预测的停顿时间模型
  6. Java堆分为大小相等的独立区域(Region),优先回收价值最大的Region;新年代和老年代不再是物理隔离的了
  7. Remembered Set:记录相关的引用信息
  1. 每个Region都有一个Remembered Set
  2. 目的:扫描一个Region时,避免了全栈扫描(RegionRegion之间的引用可能存在关联)
  1. 不计算维护Remembered Set的操作,步骤:
  1. 初始标记:标记GC Roots能直接关联到的对象,并修改TAMSNext Top at Mark Start)的值
  2. 并发标记:对堆的对象进行可达性分析
  3. 最终标记:修正并发标记阶段中发生对象变化的变动
  4. 筛选回收:
  1. 理解GC日志
  2. 使用垃圾收集器参数
参数
描述
UseSerialGC
虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial+Serial Old的收集器组合进行内存回收
UseParNewGC
打开此开关后,使用ParNew+Serial Old的收集器组合进行内存回收
UseConcMarkSweepGC
打开此开关后,使用ParNew+CMS+Serial Old的收集器组合进行内存回收。Serial old作为CMS出现Concurrent Mode Failure后使用
UseParallelGC
虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge+Serial OldPS MarkSWeep)的收集器组合进行内存回收
UseParallelOldGC
打开此开关后,使用Parallel Scavenge+Parallel Old收集器组合进行内存回收
SurvivorRatio
新生代中Eden区域与Survivor区域的容量比值,默认为8,代表EdenSurvivor=8:1
PretenureSizeThreshold
直接普升到老年代的对象大小
MaxTenuringThreshold
新生代普升到老年代的最大年龄
UseAdaptiveSizePolicy
动态调整Java堆中各个区域的大小以及进入老年代的年龄
HandlePromotionFailure
是否允许分配担保失败
ParallelGCThreads
设置并行GC时进行内存回收的线程数
GCTimeRatio
MaxGCPauseMills
CMSInitiatingOccupancyFarction
UseCMSCompactAtFullCollection
CMSFullGCsBeforeCompaction
六、内存分配和回收策略
  1. 对象分配:一般在堆上分配,也可能经过JIT编译后被拆散为标量类型并间接地在栈上分配;对象分配主要在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配
  2. 对象优先在Eden区分配
  1. Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC(新生代GC
  1. 老年代GCMajor GC/Full GC
  1. –XX:+PrintGCDetails:打印内存回收日志,
  1. 大对象直接进入老年代
  1. –XXPretenureSizeThreshold:大于该值的对象直接分配到老年代
  1. 长期存活的对象将进入老年代
  1. 虚拟机给每个对象定义一个对象年龄(Age)计数器
  2. –XX:MaxTenuringThreshold:对象被分配老年代的阀值
  1. 动态对象年龄判断
  1. 若在Survivor空间中相同年龄的所有对象占用的内存大于Survivor空间的一半,那么凡是大于该年龄的对象直接进入老年代
  1. 空间分配担保
  1. 当老年代最大可用的连续空间小于新生代所有对象的总空间大小,那么Minor GC可能不安全
  2. –XX-HandlePromotionFailure:是否允许空间分配担保
  1. 允许:若最大可用连续空间大于历次晋升到老年代的对象的平均大小,允许尝试一次Minor GC
  2. 否则:进行一次Full GC