JVM和垃圾回收

总结了JVM的内存区域、垃圾回收算法、常用垃圾回收器,期待新的G1和ZGC。

1. JVM 内存区域

运行时数据区域

image-20191228120703603

堆和栈

image-20191228120807804

对象的内存布局和访问定位

image-20191228120957339

image-20191228121512559

  • 类加载检查
  • 分配内存:指针碰撞、空闲列表
  • 初始化零值
  • 设置对象头:类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息
  • 执行init方法

句柄和直接引用

image-20191228121105513

内存参数设置和溢出

溢出类型参数设置报错信息
堆溢出-Xms
-Xmx
java.lang.OutOfMemoryError: GC overhead limit exceeded 不断分配对象
java.lang.OutOfMemoryError: Java heap space 分配巨型对象
新生代配置-Xmn
-XX:NewRatio
-XX:SurvivorRatio
方法区和运行时常量池溢出-XX:MetaspaceSize
-XX:MaxMetaspaceSize
java.lang.OutOfMemoryError
本地方法栈和虚拟机栈溢出-Xssjava.lang.StackOverflowError
本机直接内存溢出-XX:MaxDirectMemorySizeException in thread "main" java.lang.OutOfMemoryError: Direct buffer memory

JDK提供的分析工具

作用示例
jps虚拟机进程状况工具jps [-q] [-mlvV]
jstat虚拟机统计信息监视工具jstat -gc
jstat -gccause
jstat -gcutil <pid>
jinfoJava配置信息工具jinfo -flags <pid>
jinfo -sysprops <pid>
jinfo -flag +PrintGC <pid>
jmapJava内存映像工具jmap -dump:live,format=b,file=heap.bin <pid>
jhat虚拟机堆转储快照分析工具分析jmap生成的转储文件
jstackJava堆栈跟踪工具线程栈信息,死锁检测,Thread.getAllStackTraces()
JConsoleJava监视与管理控制台
JVisualVM多合一故障处理工具比jconsole更强大,支持插件
jmc飞行记录器,监控数据比jvisualvm更加丰富

2. 垃圾回收和内存分配策略

  • Where回收谁:堆内存、常量池,引用类型

  • When什么时候回收:内存空间不足、

  • How怎么回收:GC算法

判断对象存活

原理优劣
引用计数记录对象被引用次数,为0时则不可用方便简单,但存在相互引用的问题
可达性分析以GC Roots作为起始点开始搜索,搜索路径称为引用链,对象不与引用链相连则此对象不可用

GC Roots

  • 虚拟机栈中引用的对象
  • 本地方法栈中JNI引用的对象
  • 方法区中类型静态属性引用的对象
  • 方法区中常量引用的对象

引用类型

示例说明和回收时机
强引用Object obj = new Object()
软引用
SoftReference
SoftReference<User> user = new SoftReference<>(new User());有用非必需的对象,即将OOM之前被回收,用在内存资源紧张、缓存
弱引用
WeakReference
WeakReference<User> user = new WeakReference<>(new User());稍微有用但非必需,GC发生即被回收,用在缓存、HashMap
虚引用
PhantomReference
一般使用不上,析构函数finalize不建议使用通过虚引用无法拿到对象实例,被回收时收到一个通知

垃圾回收算法

工作原理优劣劣势
Mark-Sweep
标记清除算法
先标记出需回收的对象,然后统一回收简单空间碎片
Copying
复制算法
内存划分为两部分,将活着的对象复制到另一部分空间连续浪费一半空间、复制数据
Mark-Compact
标记整理算法
先标记出需回收的对象,然后把向一端移动空间连续复制数据

分代收集

根据对象存活周期将内存分块,应用不同的收集算法:

  • 新生代:Eden : Survivor : Survivor = 8 : 1 : 1 复制算法,minor gc
  • 老年代:标记清除或标记整理,full gc

垃圾回收器

img

并行:垃圾收集的多线程的同时进行。

并发:垃圾收集的多线程和应用的多线程同时进行。

算法收集器类型特性和适用场景
Serial GC新生代,复制算法单线程stop the world,简单高效
ParNew新生代,复制算法并行多线程关注停顿时间,搭配CMS首选,适合用户交互
Parallel Scavenge新生代,复制算法并行多线程关注吞吐量,适合后台任务
Serial Old老年代,标记整理算法单线程
Parallel Old老年代,标记整理算法并行多线程关注吞吐量,搭配Parallel Scavenge
CMS老年代,标记清除算法并行并发关注停顿时间

Serial + Serial Old :+1:单CPU

基于串行回收的垃圾回收器适用于大多数对于暂停时间要求不高的 Client 模式下的 JVM

img

ParNew + Serial Old

Parallel Scavenge + Serial Old

img

Parallel Scavenge + Parallel Old :+1:高吞吐量

程序吞吐量优先的应用场景中,在 Server 模式下内存回收的性能较为不错

image-20191223000908763

ParNew + CMS :+1:低停顿时间

并发低延迟,吞吐量较低。经过CMS收集的堆会产生空间碎片,会带来堆内存的浪费

初始标记 -> 并发标记 -> 重新标记 -> 并发清除

img

G1 :+1:全能型选手​

基于并行和并发、低延迟以及暂停时间更加可控的区域化分代收集的垃圾回收器

没有采用传统物理隔离的新生代和老年代的布局方式,仅仅以逻辑上划分为新生代和老年代,选择的将 Java 堆区划分为 2048 个大小相同的独立 Region 块

img

参数新生代老年代适用场景
-XX:+UseSerialGCSerialSerial Old单CPU
-XX:+UseParNewGCParNewSerial Old
-XX:+UseParallelGCParallel ScavengeSerial Old
-XX:+UseParallerOldGCParallel ScavengeParallel Old后台计算,高吞吐量
-XX:+UseConcMarkSweepGCParNewCMS用户交互,低停顿时间
-XX:+UseG1GC

Stop The World

暂停用户线程进行垃圾回收的现象。调优的目标就是尽可能的减少STW的时间和次数

内存分配与回收策略

  • 对象优先在Eden分配:空间不足则Minor GC
  • 大对象直接进入老年代:如长字符串或大数组
  • 长期存活的对象进入老年代:默认15,-XX:MaxTenuringThreshold调整
  • 动态对象年龄判定:survivor中相同年龄的对象总和大于空间的一半时直接进入老年代
  • 空间分配担保:survivor空间不足,无法容纳的对象直接进入老年代,老年代的连续空间大于新生代对象的总大小或者历次晋升的平均大小,就进行Minor GC,否则FullGC

内存泄漏、内存溢出

OOM

  • 内存溢出:内存空间不足导致
  • 内存泄漏:该释放的对象没有释放

GC调优的原则和步骤

  1. 大部分的java应用不需要调优
  2. 优先改善代码问题
  3. GC调优时最后的手段
    1. 选择合适的GC收集器
    2. 选择合适的堆大小
    3. 选择合适的年轻代在堆中的占比

步骤

  1. 监控GC状态
  2. 分析结果,判断是否要优化
    • Minor GC时间 <50ms, 频率10s
    • Full GC时间执行时间 <1s,频率10min

 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×