成都企业网站优化,网络维护与管理,wordpress 秒拍,做家装的网站有什么区别背景
XOP服务运行期间#xff0c;查看Grafana面板#xff0c;发现堆内存周期性堆积#xff0c;Full GC时间略长#xff0c;需要调查下原因 目录
垃圾收集器概述 常见的垃圾收集器分区收集策略为什么CMS没成为默认收集器 查看JVM运行时环境分析快照 PhantomReference虚引用…背景
XOP服务运行期间查看Grafana面板发现堆内存周期性堆积Full GC时间略长需要调查下原因 目录
垃圾收集器概述 常见的垃圾收集器分区收集策略为什么CMS没成为默认收集器 查看JVM运行时环境分析快照 PhantomReference虚引用
1、垃圾收集器概述
常见的垃圾收集器
按照收集策略划分
新生代收集器Serial、ParNew、Parallel Scavenge老年代收集器Serial Old、Parallel Old、CMS整堆分区收集器G1、ZGC、Shenandoah
吞吐量优先、停顿时间优先
吞吐量优先Parallel Scavenge收集器、Parallel Old 收集器。停顿时间优先CMSConcurrent Mark-Sweep收集器。
吞吐量与停顿时间适用场景
吞吐量优先交互少计算多适合在后台运算的场景。停顿时间优先交互多对响应速度要求高
串行、并行、并发
串行Serial、Serial Old垃圾回收必须暂停全部工作线程无法利用多核优势。并行ParNew、Parallel Scavenge、Parallel Old并行描述的是多条垃圾收集器线程之前的关系说明同一时间有多条垃圾收集器线程在工作此时用户线程默认是处于等待状态。并发CMS、G1并发描述的是垃圾收集器线程和用户线程之间的关系
算法参考往期博客
复制算法Serial、ParNew、Parallel Scavenge、G1标记-清除CMS标记-整理Serial Old、Parallel Old、G1
通过参数选择需要使用的垃圾收集器
-XX:UseSerialGC虚拟机运行在Client模式下的默认值SerialSerial Old。-XX:UseParNewGCParNewSerial Old在JDK1.8被废弃在JDK1.7还可以使用。-XX:UseConcMarkSweepGCParNewCMSSerial Old。-XX:UseParallelGC虚拟机运行在Server模式下的默认值Parallel ScavengeSerial Old(PS Mark Sweep)。-XX:UseParallelOldGCParallel ScavengeParallel Old。-XX:UseG1GCG1G1。
分区收集策略
JDK7/8使用采用分代收集比较多的垃圾收集器组合
另外随着JDK版本更新JDK9之后默认的垃圾收集器为G1之前分代收集思想逐渐被分区收集思想代替考虑的是收集堆内存的哪个部分才能获得收益最大如G1、ZGC-JDK15开始准备好生产了、Shenandoah并且随着JDK的版本的升级吞吐量、响应速度都在不断优化提升。
G1开创了垃圾收集器面向局部收集的设计思路 和 基于Region的内存布局形式。不再像之前那样划代而是把连续的堆内存划分为一块块的Region每一个Region都可以根据需要充当之前分代区域的Eden、Survivor、老年代空间除此之外还有一类特殊的Humongous区域专门用于存储大对象。它可以面向堆内存任何部分来组成回收集Collection Set衡量标准不再是它属于哪个分代而是哪块内存中存放的垃圾数量最多回收收益最大优先处理回收收益最大的那些 Region这也就是 Garbage First 名字的由来。Shennandoah一款RedHat独立开发后来贡献给OpenJDK的收集器在OracleJDK不存在目标之一是暂停时间与堆大小无关并且经过优化中断时间不超过几毫秒。ZGC目标和Shennandoah类似希望在对吞吐量影响不大的情况下相比G1应用程序吞吐量减少不超过15%实现任意堆内存大小都可以吧垃圾收集器停顿时间限制在十毫秒内。
深入学习参考
GC - Java 垃圾回收器之G1详解Java Hotspot G1 GC的一些关键技术-美团技术团队新一代垃圾回收器ZGC的探索与实践-美团技术团队
为什么CMS从来没成为默认收集器
CMSConcurrent Mark Sweep是一种 以获取最短回收停顿时间为目标 的收集器。 在 JDK 5 发布时HotSpot 虚拟机推出了一款在强交互应用中具有划时代意义的垃圾收集器——CMS 收集器。这款收集器是 HotSpot 中第一款真正意义上支持并发的垃圾收集器它首次实现了让垃圾收集线程与用户线程基本上同时工作。 CMS 比 G1 早不了多少。CMS 从 JDK 5 开始加入6 成熟而 G1 是 7 加入8 成熟9 正式成为默认 GC 策略。此时 CMS 就被标记为 Deprecated随后在 JDK 14 中被移除。
CMS并不是一个非常成功的GC策略GC优化一般考虑点是吞吐量和响应时间而CMS
采用标记-清除算法当处理器核比较少的时候会造成比较大的负载而且容易产生内存碎片碎片太多无能为力的时候触发Concurrent Mode Failure还需要Serial Old来擦屁股。仅针对老年代还需要一个新生代收集器但是和Parallel Scavenage又不兼容只能选择性能不如Parallel Scavenage的PerNew。需要调整的参数比较多比G1多一倍
以上的种种造成的结果就是 ParNew CMS Serial Old 的组合工作起来其实并不稳定。为了得到 CMS 那一点好处需要付出很多的代价包括 JVM 调参。 CMS 相比前辈们没有带来革命性的改变而它的后辈们比它强太多。它自身的实现又很复杂兼容性又差调参也很麻烦所以无法成为默认 GC 方案了。
参考
Java——七种垃圾收集器JDK11最新ZGC 面试JVM 垃圾回收器-腾讯云开发者社区-腾讯云Java 8 vs Java 17 垃圾收集器
2、查看JVM运行时环境
数据库环境是MySQL连接池使用的是HikariPool驱动是mysql-connector-java-8.0.21.jar当前生产环境JVM运行参数
root 14968 1 0 2月23 ? 00:41:51 java -server -XX:MetaspaceSize160m
-XX:MaxMetaspaceSize160m
-Xms1024m
-Xmx1024m
-Xss256k
-Duser.timezoneGMT08
-XX:UseParNewGC
-XX:UseConcMarkSweepGC
-XX:CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction80
-XX:UseCMSInitiatingOccupancyOnly
-XX:HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath/data/serviceroot/xkw-xopqbm-api-service/xkw-xopqbm-api-service.hprof
-XX:PrintGCDetails
-XX:PrintGCDateStamps
-Xloggc:/data/serviceroot/xkw-xopqbm-api-service/logs/xkw-xopqbm-api-service-gc.log
-XX:UseGCLogFileRotation
-XX:NumberOfGCLogFiles10
-XX:GCLogFileSize1m -jar /data/serviceroot/xkw-xopqbm-api-service/xkw-xopqbm-api-service.jar
--spring.profiles.activetest
--server.port9501
-Dons.client.logLevelERROR当前服务器的java版本为1.8
[rootlocalmdmtest ~]# java -version
java version 1.8.0_281
Java(TM) SE Runtime Environment (build 1.8.0_281-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)查看使用的垃圾收集器 jmap -heap pid可以看到当前使用的垃圾收集器 ParNewCMSSerial Old
[rootlocalmdmtest ~]# jmap -heap 14968Attaching to process ID 14968, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.281-b09using parallel threads in the new generation.
using thread-local object allocation.# Concurrent Mark-Sweep GC CMS回收器
# Mark Sweep Compact GC 串行GCSerial GC
# Parallel GC with 2 thread(s) 并行GCParNew# 这里看出是CMS
Concurrent Mark-Sweep GCHeap Configuration:MinHeapFreeRatio 40MaxHeapFreeRatio 70MaxHeapSize 1073741824 (1024.0MB)NewSize 357892096 (341.3125MB)MaxNewSize 357892096 (341.3125MB)OldSize 715849728 (682.6875MB)NewRatio 2SurvivorRatio 8MetaspaceSize 167772160 (160.0MB)CompressedClassSpaceSize 159383552 (152.0MB)MaxMetaspaceSize 167772160 (160.0MB)G1HeapRegionSize 0 (0.0MB)Heap Usage:
New Generation (Eden 1 Survivor Space):capacity 322109440 (307.1875MB)used 245758352 (234.37342834472656MB)free 76351088 (72.81407165527344MB)76.2965382200534% used
Eden Space:capacity 286326784 (273.0625MB)used 239080888 (228.00530242919922MB)free 47245896 (45.05719757080078MB)83.49930965592098% used
From Space:capacity 35782656 (34.125MB)used 6677464 (6.368125915527344MB)free 29105192 (27.756874084472656MB)18.66117484403617% used
To Space:capacity 35782656 (34.125MB)used 0 (0.0MB)free 35782656 (34.125MB)0.0% used
concurrent mark-sweep generation:capacity 715849728 (682.6875MB)used 84539240 (80.6229019165039MB)free 631310488 (602.0645980834961MB)11.809634996466745% used45864 interned Strings occupying 4725904 bytes.3、分析快照
dump快照命令jmap -dump:live,formatb,file/home/scl/xopqbm/heapdump.hprof xxx可以使用MAT或者在线工具https://heaphero.io/分析快照
发现11,319 instances of com.mysql.cj.jdbc.AbandonedConnectionCleanupThread$ConnectionFinalizerPhantomReference 也就是ConnectionFinalizerPhantomReference占了80%的堆内存为什么会这么多对象需要分析下原因。
PhantomReference虚引用
ConnectionFinalizerPhantomReference这个类在AbandonedConnectionCleanupThread类内定义继承PhantomReference
// AbandonedConnectionCleanupThread类内
private static class ConnectionFinalizerPhantomReference extends PhantomReferenceMysqlConnection {private NetworkResources networkResources;ConnectionFinalizerPhantomReference(MysqlConnection conn, NetworkResources networkResources, ReferenceQueue? super MysqlConnection refQueue) {super(conn, refQueue);this.networkResources networkResources;}void finalizeResources() {if (this.networkResources ! null) {try {this.networkResources.forceClose();} finally {this.networkResources null;}}}}对于PhantomReference虚引用的概念简单就是他可以将某个对象标记为虚的一般用于标记对象是否被GC回收虚引用也称为“幽灵引用”它是最弱的一种引用关系。 如果一个对象仅持有虚引用那么它就和没有任何引用一样在任何时候都可能被垃圾回收器回收。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。当垃圾回收器准备回收一个对象时如果发现它还有虚引用就会在垃圾回收后将这个虚引用加入引用队列在其关联的虚引用出队前不会彻底销毁该对象。所以可以通过检查引用队列中是否有相应的虚引用来判断对象是否已经被回收了。
查看AbandonedConnectionCleanupThread类内部的属性和主要的run方法主要的属性有
Set connectionFinalizerPhantomRefsReferenceQueue referenceQueue
private static final SetConnectionFinalizerPhantomReference connectionFinalizerPhantomRefs ConcurrentHashMap.newKeySet();private static final ReferenceQueueMysqlConnection referenceQueue new ReferenceQueue();private static final ExecutorService cleanupThreadExcecutorService;private static Thread threadRef null;private static Lock threadRefLock new ReentrantLock(); public void run() {for (;;) {try {checkThreadContextClassLoader();Reference? extends MysqlConnection reference referenceQueue.remove(5000);if (reference ! null) {finalizeResource((ConnectionFinalizerPhantomReference) reference);}} catch (InterruptedException e) {threadRefLock.lock();try {threadRef null;// Finalize remaining references.Reference? extends MysqlConnection reference;while ((reference referenceQueue.poll()) ! null) {finalizeResource((ConnectionFinalizerPhantomReference) reference);}connectionFinalizerPhantomRefs.clear();} finally {threadRefLock.unlock();}return;} catch (Exception ex) {// Nowhere to really log this.}}
}// TODO
参考
JVM 优化踩坑记数据库连接池引起的FullGC问题看我如何一步步排查、分析、解决PhantomReference 引发的GC问题-CSDN博客AbandonedConnectionCleanupThread$ConnectionFinalizerPhantomReference内存溢出_abandoned connection cleanup thread-CSDN博客G1垃圾回收参数调优及MySQL虚引用造成GC时间过长分析 | 京东云技术团队 - 墨天轮