JVM中垃圾回收的内存区域主要指堆。虽然HotSpot对方法区也会进行垃圾回收,但是不在JVM的规范里。通过垃圾回收调优,尽可能减少垃圾回收的次数和时间,是我们的目标。
垃圾收集器需要完成两部分工作:判断对象是否存活和回收内存。
判断对象是否存活
JVM从所有GCRoot对象出发,遍历他们引用的对象。没被遍历到的对象表示已经死亡。
GCRoot对象包括:
- 虚拟机栈中引用到的对象
- 本地方法栈中引用到的对象
- 方法区中静态变量引用到的对象
- 方法去中常量引用到的对象
回收方法
- 复制-清除
将内存分为大小相等的两块。将其中一块A作为工作区,当A可用内存不够时,将存活对象复制到另一块B,并将其改为工作区,清除A中的所有对象回收内存。当B可用内存不够时,将存活对象复制到A,并将A改为工作区,周而复始。缺点是可用内存最多只有全部内存的一半。 - 标记-清除(Mark-Sweep)
缺点是造成内存碎片过多,可能无法存放大对象 - 标记-整理(Mark-Compact)
在标记-清除的基础上将存活对象移动到一块。缺点是耗时长 - 分代
这是JVM采用的回收方法,综合了前面的三种方法。
在新生代中采用复制-清除,在年老代中采用标记-清除或标记-整理,取决于使用的垃圾收集器:
- 刚new的对象保存在eden
- eden空间用完以后,进行Minor GC,将eden中的对象复制到to空间,并清空eden空间,再把to和from空间互换
- 继续new对象保存在eden,eden空间用完后,进行Minor GC,将from和eden中的对象复制到to空间,并清空eden和from空间,再把to和from空间互换
- 如果对象经历的Minor GC次数超过了一定的阈值threshold,就移到年老代中
- 年老代中空间用完就会触发Major GC,对年老代进行垃圾回收
垃圾收集器
JVM常见的垃圾收集器有以下几种:
名称 | 作用空间 | 方法 | 运行方式 | 启用参数 | 特点 |
---|---|---|---|---|---|
Serial | 新生代 | 复制-清除 | 单线程,串行 | -XX:+UseSerialGC | |
ParNew | 新生代 | 复制-清除 | 多线程,并行 | -XX:+UseParNewGC | 停顿时间短 |
Parallel Scavenge | 新生代 | 复制-清除 | 多线程,并行 | -XX:+UseParallelGC | 吞吐量高 |
Serial Old | 年老代 | 标记-整理 | 单线程,串行 | -XX:+UseSerialGC | CMS备用 |
Parallel Old | 年老代 | 标记-整理 | 多线程,并行 | -XX:+UseParallelOldGC | 吞吐量高 |
CMS | 年老代 | 标记-清除 | 多线程,并发 | -XX:+UseConcMarkSweepGC | 停顿时间短 |
除CMS外的其他收集器工作时都会使应用停止运行。CMS实现了垃圾收集器线程与用户线程同时工作,实现了最短停顿时间。因为CMS采用了标记-清除方法,所以CMS会造成内存碎片。在应用产生死亡对象的速度高于回收速度或者内存碎片过多无法满足大对象要求时,CMS回收会失败,这个时候会运行一次Serial Old收集器。
- 在与用户交互比较多,追求响应速度的场景下,选择ParNew和CMS
- 在进行后台数据处理,追求吞吐量的场景下,选择Parallel Scavenge和Parallel Old
垃圾收集器调优
- -XX:ParallelGCThreads=10 并行收集器的线程数
- -XX:MaxTenuringThreshold=31 经过31次Minor GC的对象放入到年老代中,这里只是指定最大值,实际阈值通过XX:TargetSurvivorRatio计算得到
- -XX:PretenureSizeThreshold=1024 单位字节,超过一定大小的对象直接放入年老代,不放入to
- -XX:TargetSurvivorRatio=50 把from中经过Minor GC次数最多的对象移到年老代中,直到最后to已用空间的大小小于to空间的50%
- -XX:+UseCMSCompactAtFullCollection CMS收集器运行完后是否进行内存整理,减少内存碎片,默认打开
- -XX:CMSFullGCsBeforeCompaction=5 CMS收集器每运行6次就执行一次内存整理,默认0,也就是每次都执行
- -XX:CMSInitiatingOccupancyFraction=60 CMS收集器在年老代使用空间超过60%时进行垃圾回收
- -XX:+CMSClassUnloadingEnabled CMS收集器在方法区进行垃圾回收
- -XX:+DisableExplicitGC 禁止显式GC,System.gc()
注意 相关垃圾回收信息可以通过jstat获得
补充
从jdk1.7开始,JVM支持G1垃圾回收器。G1将整个内存空间分成多个region,机动分配给新生代和年老代,垃圾回收线程优先回收垃圾最多的region。回收效率低于CMS,但是不会和CMS一样,因为内存不够就不停的fullGC,造成垃圾回收雪崩的现象。G1最大的特点是能够设定应用的停滞时间,默认200ms,比较适合于分布式流式数据处理系统。使用方式-XX:+useG1GC