文章目录
GC介绍
1.什么是垃圾
垃圾的定义:没有任何引用指向的一个对象或者多个对象(循环引用)
2.如何定位垃圾
引用计数(ReferenceCount)内存上有一块空间记录被引用的次数
根可达算法(RootSearching)
3.常见的垃圾回收算法
标记清除(mark sweep) - 位置不连续 产生碎片 效率偏低(两遍扫描)
拷贝算法 (copying) - 没有碎片,浪费内存空间
标记压缩(mark compact) - 没有碎片,效率偏低(两遍扫描,指针需要调整)
4.JVM内存分代模型(用于分代垃圾回收算法)
5.常用的垃圾收集器
Serial
Serial是历史比较久远、方式比较直接的收集器。它是一个单线程工作的收集器,在Serial工作期间要停止所有的业务线程(STW),等待Serial工作完毕,业务线程再开始工作。
使用标记-复制算法收集新生代垃圾。
Serial Old
Serial Old是Serial 收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。
同样是在工作期间,停止所有业务线程
Parsllel Scavenge
Parsllel Scavenge是新生代的垃圾收集器,采用标记-复制算法。但是他是一个多线程的,其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是提高吞吐量(吞吐量 = 运行用户程序的时间 / (运行用户程序的时间 + 垃圾收集的时间))。
Parallel Old
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,支持多线程并行收集。采用标记-整理算法实现。
Parsllel Scavenge与Parallel Old是JDK1.8版本默认的垃圾收集器
CMS
CMS(Concurrent Mark Sweep),收集器几乎占据着 JVM 老年代收集器的半壁江山,它划时代的意义就在于垃圾回收线程几乎能做到与用户线程同时工作。
使用标记-清除算法收集老年代垃圾。
工作流程主要有如下 4 个步骤:
- 初始标记: 仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿(Stop-the-world)
- 并发标记: 进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿
- 重新标记: 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿(Stop-the-world)
- 并发清除: 清理垃圾,不需要停顿
在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。
ParNew
工作在新生代,本质为Serial收集器的多线程版本,采用“复制算法”;只有它能与CMS收集器配合工作
G1
该收集器可以算得上是垃圾收集器技术发展历史上里程碑式的成果。我们之前聊的垃圾收集器,垃圾收集的目标要么是针对整个老年代(Major GC),要么就是针对整个新生代(Minor GC),要么就好似整个Java堆进行回收(Full GC)。但是G1则提出了另一种内存布局形式–基于Region的内存布局形式。当然这种内存布局形式也是基于分代理论设计的,但是它不再坚持固定大小以及固定数量的分代区域划分,而是将连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以扮演新生代的Eden空间,Survivor空间,或者老年代空间。当然对于一个超过了Region容量一半的对象,会被判定为大对象,将会用特殊的Humongous来进行存储,如果对象过大则会存储在连续的Humongous中,G1的大多数行为会将Humongous区域当作老年代来看待。
使用复制 + 标记 - 整理算法收集新生代和老年代垃圾。
二、调用工具
1.常见调优命令
jps:虚拟机进程状况工具 选项 作用
-q 只输出LVMID,省略主类名称
-m 输出虚拟机进程启动是传递给主类main()函数的参数
-l 输出主类名
-v 输出虚拟机进程启动时JVM参数
[admin@d-metaverse ~]$ jps -l
8611 /home/admin/metaverse-backend-dev/metaverse-provider-cim-1.0-SNAPSHOT.jar
9572 /home/admin/metaverse-backend-dev/metaverse-provider-login-1.0-SNAPSHOT.jar
1797 /home/admin/metaverse-backend-dev/metaverse-provider-stepbar-1.0-SNAPSHOT.jar
8421 /home/admin/metaverse-backend-dev/metaverse-gateway-1.0-SNAPSHOT.jar
8268 /home/admin/metaverse-backend-dev/metaverse-provider-timing-1.0-SNAPSHOT.jar
9933 /home/admin/metaverse-backend-dev/metaverse-provider-course-1.0-SNAPSHOT.jar
8879 /home/admin/metaverse-backend-dev/metaverse-provider-activity-1.0-SNAPSHOT.jar
9746 /home/admin/metaverse-backend-dev/metaverse-provider-subassembly-1.0-SNAPSHOT.jar
10258 /home/admin/metaverse-backend-dev/metaverse-provider-pushclass-1.0-SNAPSHOT.jar
10067 /home/admin/metaverse-backend-dev/metaverse-provider-synchronization-1.0-SNAPSHOT.jar
9018 /home/admin/metaverse-backend-dev/metaverse-provider-fileupload-1.0-SNAPSHOT.jar
9338 /home/admin/metaverse-backend-dev/metaverse-provider-chapter-1.0-SNAPSHOT.jar
11771 jdk.jcmd/sun.tools.jps.Jps
11518 /home/admin/metaverse-backend-dev/metaverse-provider-class-1.0-SNAPSHOT.jar
jstat(JVM statistice Monitoring Tool):虚拟机统计信息监视工具
-class 监视类加载、下载数量、总空间等
-gcutil 与-gc 输出一直,主要关注已使用空间占总空间的占比
-gc 监视堆状况,包括Eden、Survivor、老年代、永久代等
[admin@t-points ~]$ jstat -class 1351
Loaded Bytes Unloaded Bytes Time
13693 25173.1 0 0.0 74.57
[admin@t-points ~]$ jstat -gc 1351
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
7168.0 8704.0 6240.0 0.0 157184.0 78732.0 349696.0 41656.1 76720.0 73530.4 9904.0 9255.2 28 1.486 3 1.485 2.971
[admin@t-points ~]$ jstat -gcutil 1351
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
87.05 0.00 50.09 11.91 95.84 93.45 28 1.486 3 1.485 2.971
2.可视化工具
1、JConsole:java监视与管理控制台
JConsole是一款基于JMX的可视化监视、管理工具。
JConsole在javahome/bin目录下,jconsole.exe
三、JVM调优策略
1.选择合适的垃圾回收器
CPU单核,那么毫无疑问Serial 垃圾收集器是你唯一的选择。 CPU多核,关注吞吐量 ,那么选择PS+PO组合。
CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,那么选择CMS。
CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。
//设置Serial垃圾收集器(新生代)
开启:-XX:+UseSerialGC
//设置PS+PO,新生代使用功能Parallel Scavenge 老年代将会使用Parallel Old收集器
开启 -XX:+UseParallelOldGC
//CMS垃圾收集器(老年代)
开启 -XX:+UseConcMarkSweepGC
//设置G1垃圾收集器
开启 -XX:+UseG1GC
2.调整内存大小
现象:垃圾收集频率非常频繁。
原因:如果内存太小,就会导致频繁的需要进行垃圾收集才能释放出足够的空间来创建新的对象,所以增加堆内存大小的效果是非常显而易见的。
注意:如果垃圾收集次数非常频繁,但是每次能回收的对象非常少,那么这个时候并非内存太小,而可能是内存泄露导致对象无法回收,从而造成频繁GC。
- 参数配置:
//设置堆初始值
指令1:-Xms2g
指令2:-XX:InitialHeapSize=2048m
//设置堆区最大值
指令1:`-Xmx2g`
指令2: -XX:MaxHeapSize=2048m
//新生代内存配置
指令1:-Xmn512m
指令2:-XX:MaxNewSize=512m
3.调整大对象的标准
现象:老年代频繁GC,每次回收的对象很多,而且单个对象的体积都比较大。
原因:如果大量的大对象直接分配到老年代,导致老年代容易被填满而造成频繁GC,可设置对象直接进入老年代的标准。
注意:这些大对象进入新生代后可能会使新生代的GC频率和时间增加。
4.调整GC的触发时机
现象:CMS,G1 经常 Full GC,程序卡顿严重。
原因:G1和CMS 部分GC阶段是并发进行的,业务线程和垃圾收集线程一起工作,也就说明垃圾收集的过程中业务线程会生成新的对象,所以在GC的时候需要预留一部分内存空间来容纳新产生的对象,如果这个时候内存空间不足以容纳新产生的对象,那么JVM就会停止并发收集暂停所有业务线程(STW)来保证垃圾收集的正常运行。这个时候可以调整GC触发的时机(比如在老年代占用60%就触发GC),这样就可以预留足够的空间来让业务线程创建的对象有足够的空间分配。
注意:提早触发GC会增加老年代GC的频率。