1 JVM 运行时数据区
1.1 Program Counter Register
当前线程所执行字节码的行号指示器
不会出现 OutOfMemoryError
1.2 VM Stack
- 线程私有,生命周期和线程同
- 方法执行时,同步创建一个栈帧
局部变量表,操作数栈,动态连接,方法出口
- 局部变量表
存储基本数据类型,对象引用,returnAddress
局部变量表最小单位:slot
long 和 double 占用 2个 slot
异常情况
- StackOverflowError:超出JVM设置栈深度
- OutOfMemoryError:栈扩展时无法申请到足够的内存 常用参数
- -Xss 线程栈空间大小
1.3 Native Method Stack
JVM 执行 native 方法时使用
异常情况
- StackOverflowError:超出JVM设置栈深度
- OutOfMemoryError:栈扩展时无法申请到足够的内存
1.4 Java 堆
异常情况
- OutOfMemoryError:堆无法扩展时
出现原因
不断地创建对象, 并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象
// 堆异常信息
java.lang.OutOfMemoryError: Java heap space
处理思路
- 明确是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)
- Memory Leak
通过工具查看泄漏对象到GC Roots的引用链
找到泄漏对象是通过怎样的引用路径、 与哪些GC Roots相关联
根据泄漏信息及GC Roots引用链信息,定位到存在泄漏代码的具体位置
常用参数
- -Xms 初始堆大小
- -Xmx 最大堆大小
- -Xmn 年轻代空间大小
- -XX:SurvivorRatio 设置新生代中Eden和一个Survivor 空间比
- -XX:PretenureSizeThreshold 对象大于设定值直接在老年代分配(Serial,ParNew 有效)
- -XX:MaxTenuringThreshold 设置进入老年代的年龄阈值(最大值15)
- Memory Overflow
检查 JVM 堆参数,是否还有上调空间
从代码上检查是否有对象生命周期过长,持有状态时间过长,存储结构设计不合理等情况
1.5 Method Area
存储已被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据 异常情况
- OutOfMemoryError:方法区内存不足时
出现原因
运行时产 生大量的类去填满方法区, 直到溢出为止。
java.lang.OutOfMemoryError: Metaspace
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348)
java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.objectweb.asm.MethodWriter.visitLabel(MethodWriter.java:1239)
处理思路
增加元空间大小
常用参数
- -XX:MetaspaceSize 初始元空间
- -XX:MaxMetaspaceSize 最大元空间
- -XX:MinMetaspaceFreeRatio 在垃圾收集之后控制最小的元空间剩余容量的百分比
- -XX:MaxMetaspaceFreeRatio 控制元空间剩余最大容量百分比
1.6 运行时常量池
方法区的一部分
异常情况
- OutOfMemoryError:存储常量到常量池,当内存不足时
出现原因
存储常量到常量池,当内存不足时
// 堆异常信息
java.lang.OutOfMemoryError: Java heap space
1.7 直接内存
NIO 使用 native 函数直接分配的堆外内存
异常情况
- OutOfMemoryError:扩展时无法申请到足够的内存
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.wzh.jvm.error.DirectMemoryOOM.main(DirectMemoryOOM.java:16)
常用参数
- -XX:MaxDirectMemorySize
2 垃圾回收
2.1 可达性分析算法
以GC Roots为起始节点集,根据引用关系向下搜索,对象不可达,即可判断为垃圾
2.1.1 GC Roots
- VM Stack 中引用的对象
- Method Area 中类静态属性引用的对象,常量引用的对象
- Native Method Stack 中 JNI 引用的对象
- JVM 内部的引用(基本类型的class对象,异常对象,类加载器)
- 被同步锁持有的对象
- 反映 JVM 内部情况的 JMXBean,JVMTI中注册的回调,本地代码缓存
2.1.2 步骤
- 根节点枚举都必须暂停用户线程(STW)
- 查找引用链(可并发执行)
2.2 引用
强引用
- 类似 Object o=new Object()
- 无论任何关系下,只要强引用存在,GC就永远不会回收被引用的对象
软引用
- SoftReference 描述一些还有用,但非必须的对象
- 在系统将要发送溢出前,会把软引用对象进行二次回收
- 如果回收后还没有足够内存,才会抛出内存溢出异常
弱引用
- WeakReference 描述非必须对象
- 弱引用关联的对象只能生存到下一次GC 发生为止
虚引用
- PhantomReference 为了能在这个对象被收集器回收时收到一个系统通知
2.3 垃圾回收分类
- Young GC:新生代的垃圾收集
- Old GC:老年代的垃圾收集 (CMS)
- Mixed GC:新生代和部分老年代的垃圾收集 (G1)
- Full GC:Java堆+方法区的垃圾收集
2.4 垃圾回收算法
2.4.1 Mark-Sweep(标记-清除)
- 多作用于新生代
- CMS 基于标记清除,工作在老年代
引入问题:
- 执行效率不稳定
- 产生碎片化空间
2.4.2 Semispace Copying(标记-复制)
- 多作用于新生代
- Serial,ParNew,Parallel Scavenge
引入问题
- 存在空间浪费
2.4.3 Mark-Compact(标记-整理)
- 多作用于老年代
- Serial Old
- Parallel Scavenge基于标记整理算法
2.5 垃圾回收算法细节
- 垃圾回收器在 GC Roots枚举时必须暂停用户线程(STW)
枚举期间根节点集合的对象引用关系不变
OopMap 用于枚举 GC Roots
OopMap 记录了栈上本地变量到堆上对象的引用关系
- JVM 在安全点的位置生成 OopMap
当垃圾回收时,设置标志位
各线程执行时主动轮询标志位(汇编指令 test %eax,0x160100)
标志位为真,线程在最近的安全点主动中断挂起
- 安全区域
线程Sleep、BLocked状态进入安全区
垃圾回收时不管在安全区域的线程
GC Roots枚举完通知线程,才可离开安全区
- Remembered Set(记忆集)
用于记录从非收集区域指向收集区域的指针集合的数据结构
用于解决分代收集中对象跨代引用
卡表时记忆集的一种实现
利用Write Barrier维护卡表的状态
2.6 垃圾收集器
2.6.1 Serial 收集器
- 最基础,最老的垃圾收集器
- 单线程收集器
- 工作在新生代
- 基于标记-复制算法实现
- 适用于运行在客户端模式下的虚拟机
2.6.2 ParNew 收集器
- 新生代支持多线程并行收集
- 工作在新生代
- 基于标记-复制算法实现
- 可以与CMS 收集器配合使用
- 适用于JDK7运行在服务端模式下的虚拟机
2.6.3 Parallel Scavenge 收集器
- 新生代支持多线程并行收集
- 工作在新生代
- 基于标记-复制算法实现
- 实现可控制吞吐量的回收算法
常用参数
- -XX:+MaxGCPauseMillis 控制最大垃圾收集停顿时间(>0 毫秒数)
- -XX:+GCTimeRatio 设置吞吐量大小
- -XX:+UseAdaptiveSizePolicy 打开自适应调节策略
2.6.4 Serial Old 收集器
- 最基础,最老的垃圾收集器x
- 单线程收集器
- 工作在老年代
- 基于标记-整理算法实现
- 适用于运行在客户端模式下的虚拟机
- JDK5之前与 Parallel Scavenge 配合使用
- 作为 CMS 发生失败时的后背方案(并发收集发生Concurrent Mode Failure使用)
2.6.5 Parallel Old 收集器
- 支持多线程并发收集
- 工作在老年代
- 基于标记-整理算法实现
关注吞吐量
2.6.6 CMS 收集器
- 以获取最短回收停顿时间为目标
- 基于标记-清楚算法实现
- 初始标记:标记一下GC Roots 能直接关联到的对象
- 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程
- 重新标记: 修正并发标记期间,标记产生变动的标记对象
- 并发清除:清楚标记阶段判断死亡的对象
缺陷
- CMS 对处理器的资源非常敏感
- CMS 产生浮动垃圾,可能出现 Concurrent Mode Failure,导致 STW的 FullGC
- CMS 产生碎片空间,导致 STW的 FullGC
常用参数
- -XX:CMSInitiatingOccupancyFraction 设置触发CMS的百分比
- -XX:+UseCMSCompactAtFullCollection 提前促发FullGC
- -XX:CMSFullGCsBeforeCompaction CMS执行指定次数不整理空间的FullGC之后,下一次进入FullGC时会提前整理碎片(默认0,每次都整理)
2.6.7 Garbage First收集器
- 一款主要面向服务端的垃圾收集器
- 基于Region的堆内存布局
- 哪块内存存放的垃圾数量最大,优先回收哪块内存
- 建立了可预测停顿时间模型
- G1从整体看基于标记-整理算法(OldGC),从局部看基于标记-复制算法(YoungGC)
- Region是单次回收的最小单元
- Hunongous存储大对象区域
跨Region引用对象
- 每个Region维护自己的记忆集(RSet)
- 记录下其他Region指向自己的指针
- Rest存储结构时类 哈希表
Key:其他Region起始地址
Value:集合,存储卡表的索引号
并发标记阶段出现的标记错误
- G1 通过原始快照实现
- Region中的一部分划分出来,使用两个TAMS指针标记
- 并发回收是新分配的对象地址必须要在两个 TAMS 上
- 初始标记:标记一下GC Roots 能直接关联到的对象,修改TAMS指针的值
- 并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程
- 最终标记:修正并发标记期间,遗留的STAB记录
- 筛选回收:更新Region统计数据,根据期望停顿时间制定回收计划,把存活对象复制到空的Region中,清理掉整个Region
内存回收的速度 < 内存分配数据,G1 进入 Full GC,导致长时间的STW
常用参数
- -XX:G1HeapRegionSize 设置每个Region的大小(1M~32M)
- -XX:MaxGCPauseMillis 设置垃圾回收的停顿时间(100 ~ 300 毫秒)