JVM3_数据库连接池虚引用ConnectionFinalizerPhantomReference引起的FullGC问题排查

背景

XOP服务运行期间,查看Grafana面板,发现堆内存周期性堆积,Full GC时间略长,需要调查下原因

image.png

目录

  • 垃圾收集器概述
    • 常见的垃圾收集器
    • 分区收集策略
    • 为什么CMS没成为默认收集器
  • 查看JVM运行时环境
  • 分析快照
    • PhantomReference虚引用

1、垃圾收集器概述

常见的垃圾收集器

按照收集策略划分

  • 新生代收集器:Serial、ParNew、Parallel Scavenge;
  • 老年代收集器:Serial Old、Parallel Old、CMS;
  • 整堆分区收集器:G1、ZGC、Shenandoah

吞吐量优先、停顿时间优先

  • 吞吐量优先:Parallel Scavenge收集器、Parallel Old 收集器。
  • 停顿时间优先:CMS(Concurrent 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模式下的默认值,Serial+Serial Old。
  • -XX:+UseParNewGC,ParNew+Serial Old,在JDK1.8被废弃,在JDK1.7还可以使用。
  • -XX:+UseConcMarkSweepGC,ParNew+CMS+Serial Old。
  • -XX:+UseParallelGC,虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep)。
  • -XX:+UseParallelOldGC,Parallel Scavenge+Parallel Old。
  • -XX:+UseG1GC,G1+G1。

分区收集策略

JDK7/8使用采用分代收集比较多的垃圾收集器组合

image.png另外随着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从来没成为默认收集器

CMS(Concurrent 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 223 ?       00:41:51 java -server -XX:MetaspaceSize=160m 
-XX:MaxMetaspaceSize=160m 
-Xms1024m 
-Xmx1024m 
-Xss256k 
-Duser.timezone=GMT+08 
-XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC 
-XX:+CMSParallelRemarkEnabled 
-XX:CMSInitiatingOccupancyFraction=80 
-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:NumberOfGCLogFiles=10 
-XX:GCLogFileSize=1m -jar /data/serviceroot/xkw-xopqbm-api-service/xkw-xopqbm-api-service.jar 
--spring.profiles.active=test 
--server.port=9501 
-Dons.client.logLevel=ERROR

当前服务器的java版本为1.8

[root@localmdmtest ~]# 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,可以看到当前使用的垃圾收集器 ParNew+CMS+Serial Old

[root@localmdmtest ~]# 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:    串行GC(Serial GC)
# Parallel GC with 2 thread(s): 并行GC(ParNew)# 这里看出是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,format=b,file=/home/scl/xopqbm/heapdump.hprof xxx,可以使用MAT或者在线工具https://heaphero.io/分析快照

发现11,319 instances of "com.mysql.cj.jdbc.AbandonedConnectionCleanupThread$ConnectionFinalizerPhantomReference"

image.png

也就是ConnectionFinalizerPhantomReference占了80%的堆内存,为什么会这么多对象,需要分析下原因。

PhantomReference虚引用

ConnectionFinalizerPhantomReference这个类在AbandonedConnectionCleanupThread类内定义,继承PhantomReference

// AbandonedConnectionCleanupThread类内
private static class ConnectionFinalizerPhantomReference extends PhantomReference<MysqlConnection> {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回收,虚引用也称为“幽灵引用”,它是最弱的一种引用关系。

image.png

  • 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
  • 为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。
  • 当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,将这个虚引用加入引用队列,在其关联的虚引用出队前,不会彻底销毁该对象。所以可以通过检查引用队列中是否有相应的虚引用来判断对象是否已经被回收了。

查看AbandonedConnectionCleanupThread类内部的属性和主要的run方法,主要的属性有

  • Set connectionFinalizerPhantomRefs
  • ReferenceQueue referenceQueue
private static final Set<ConnectionFinalizerPhantomReference> connectionFinalizerPhantomRefs = ConcurrentHashMap.newKeySet();private static final ReferenceQueue<MysqlConnection> referenceQueue = new ReferenceQueue<>();private static final ExecutorService cleanupThreadExcecutorService;private static Thread threadRef = null;private static Lock threadRefLock = new ReentrantLock();

image.png

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时间过长分析 | 京东云技术团队 - 墨天轮

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/727339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习目标检测】二十二、基于深度学习的肺炎检测系统-含数据集、GUI和源码(python,yolov8)

肺炎尽管很常见&#xff0c;但准确诊断是一项困难的任务。它要求训练有素的专家对胸部X光片进行检查&#xff0c;并通过临床病史&#xff0c;生命体征和实验室检查进行确认。肺炎通常表现为胸部X光片上一个或多个区域的阴影(opacity)增加。但是&#xff0c;由于肺部有许多其他状…

每日OJ题_链表①_力扣2. 两数相加

目录 力扣2. 两数相加 解析代码 力扣2. 两数相加 2. 两数相加 难度 中等 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个…

二维码门楼牌管理系统应用场景:数据管理的智慧新选择

文章目录 前言一、数据管理部门的智慧工具二、助力决策制定与优质服务提供三、二维码门楼牌管理系统的优势四、展望未来 前言 随着科技的飞速发展&#xff0c;二维码门楼牌管理系统正逐渐成为城市管理的智慧新选择。该系统不仅提升了数据管理效率&#xff0c;还为政府和企业提…

python并发编程:阻塞IO

阻塞IO&#xff08;blocking IO&#xff09; 在Linux中&#xff0c;默认情况下所有的socket都是blocking&#xff0c;一个典型的读操作流程大概是这样&#xff1a; 当用户进程调用了recvfrom这个系统调用&#xff0c;kernel就开始了IO的第一个阶段&#xff1a;准备数据。对于…

Python编程作业五:面向对象编程

目录 一、类的定义和方法 二、图书管理系统 一、类的定义和方法 定义一个学生类&#xff08;Student&#xff09;&#xff0c;包括学号(id)、姓名(name)、出生日期(birthday)和分数(score)4个属性&#xff0c;其中出生日期是私有属性&#xff0c;不能被外界直接访问。该类应具…

力扣每日一题 找出字符串的可整除数组 数论

Problem: 2575. 找出字符串的可整除数组 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public int[] divisibilityArray(String word, int m){in…

销冠MPV增配不增价,2024款腾势D9正式上市

3月6日&#xff0c;2024款腾势D9正式上市&#xff0c;官方指导价33.98万元起。销冠MPV增配不增价&#xff0c;并推出2000元定金抵扣车辆尾款10000元等上市权益。针对老用户也推出了30000元置换补贴等感恩回馈。 作为腾势汽车破局豪华MPV全品类冠军的扛鼎之作&#xff0c;腾势D9…

异地组网搭建方案

在这个信息爆炸的时代&#xff0c;人与人之间的联系变得越来越密切&#xff0c;而异地组网搭建方案也因此变得越 来越重要。无论是跨国企业、远程学习还是国际合作&#xff0c;构建一个快捷稳定的异地组网系统&#xff0c;已经 成为许多组织和个人不可或缺的需求。接下来&#…

换个角度看禅让制止于禹

前言 在历史课本上&#xff0c;对尧、舜、禹三代君王的描述是&#xff0c;他们在去世之前通过禅让制的方式把自己的王位禅让给了其他有贤明的能臣。 禅让制也是中国古代时期被无数的文人志士追捧的一个制度&#xff0c;因为他们觉得那个时代是公天下的时代。 可实际上自从人…

selenium中ChromeDriver配置,一把过,并且教你伪装

最近正值毕业季&#xff0c;我之前不是写了个问卷星代码嘛&#xff0c;昨晚上有人凌晨1点加我&#xff0c;问我相关内容。 由于我之前C盘重装了一下&#xff0c;导致我很多东西空有其表&#xff0c;实际不能用&#xff0c;借此机会&#xff0c;向大家编写ChromeDriver配置&…

libftdi库编译

目录 1. 下载源码 2. Ubuntu下编译 2.1 配置编译环境 2.2 编译 3. Android NDK下编译 3.1 编译libconfuse 3.2 编译libusb 3.3 编译libudev 3.3 编译libftdi 分2部分&#xff0c;先在Ubuntu中编译&#xff0c;然后在Android NDK中编译。 1. 下载源码 下载地址&#…

扩展学习|系统理解数字经济

文献来源&#xff1a;[1]肖静华,胡杨颂,吴瑶.成长品&#xff1a;数据驱动的企业与用户互动创新案例研究[J].管理世界,2020,36(03):183-205.DOI:10.19744/j.cnki.11-1235/f.2020.0041. [2]陈晓红,李杨扬,宋丽洁等.数字经济理论体系与研究展望[J].管理世界,2022,38(02):208-22413…

链表习题-力扣oj (附加思路版)

LCR 140. 训练计划 IIhttps://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/ 给定一个头节点为 head 的链表用于记录一系列核心肌群训练项目编号&#xff0c;请查找并返回倒数第 cnt 个训练项目编号。 思路&#xff1a;双指针&#xff0c;快指针先走cnt…

策略梯度网络

策略梯度网络是强化学习中的一类算法,它们直接学习一个策略,该策略可以映射从环境状态到采取的动作。与值函数方法(如Q学习和深度Q网络)不同,策略梯度方法通过优化策略本身来实现目标,即直接调整其输出动作的概率,以最大化累积奖励。这类方法的核心优势在于它们能够自然…

WinDbg无符号调试DriverEntry中断

无符号文件的驱动中断DriverEntry方法 当我们调试有符号的windows驱动时&#xff0c;通常可以使用bu module!DriverEntry, 在模块的DriverEntry位置打上断点。 那么对于无符号驱动&#xff0c;应该如何找到DriverEntry函数的位置呢&#xff1f; 从正常的DriverEntry的调用栈…

Java项目:40 springboot月度员工绩效考核管理系统009

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本系统的功能分为管理员和员工两个角色 管理员的功能有&#xff1a; &#xff08;1&#xff09;个人中心管理功能&#xff0c;添加管理员账号…

多线程相关面试题(2024大厂高频面试题系列)

1、聊一下并行和并发有什么区别&#xff1f; 并发是同一时间应对多件事情的能力&#xff0c;多个线程轮流使用一个或多个CPU 并行是同一时间动手做多件事情的能力&#xff0c;4核CPU同时执行4个线程 2、说一下线程和进程的区别&#xff1f; 进程是正在运行程序的实例&#xff…

XSS靶场-初级关卡

一、环境 XSS靶场 二、闯关 1、第一关 先看源码 使用DOM型&#xff0c;获取h2标签&#xff0c;使用innerHTML将内容插入到h2中 我们直接插入<script>标签试一下 明显插入到h2标签中了&#xff0c;为什么不显示呢&#xff1f;看一下官方文档 尽管插入进去了&#xff0…

【学习资源】对比说明三个通过作者查找文献数据库(一)

最近博主在阅读相关文献的时候&#xff0c;想针对一些作者的科研文献做一个详细的了解&#xff0c;于是涉及到“如何已知作者与其所在单位&#xff0c;查找其研究成果”的问题&#xff0c;博主尝试了在Google Scholar、Web of Science、CRS核心论文库这三个地方通过作者查找文献…

程序异常结束退出 无输出 无显式报错日志 爆栈

需求 开一个很大的数组&#xff08;300万&#xff09; ❗ 错误示例 #include <stdio.h>int main() {int size 3000000;int a[size];a[size-1] 999;printf("%d",a[size-1]);return 0; }&#x1f60b; 解决方案 局部变量存储在栈空间 &#xff08;较小&…