MySQL 驱动中虚引用 GC 耗时优化与源码分析
本文要点:
- 一种优雅解决 MySQL 驱动中虚引用导致 GC 耗时较长问题的解决方法
- 虚引用的作用与使用场景
- MySQL 驱动源码中的虚引用分析
public ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException { ... NonRegisteringDriver.trackConnection(this);...}
public class NonRegisteringDriver implements Driver {...protected static final ConcurrentHashMap<ConnectionPhantomReference, ConnectionPhantomReference> connectionPhantomRefs = new ConcurrentHashMap();protected static void trackConnection(com.mysql.jdbc.Connection newConn) {ConnectionPhantomReference phantomRef = new ConnectionPhantomReference((ConnectionImpl)newConn, refQueue);connectionPhantomRefs.put(phantomRef, phantomRef);}...}
尝试减少数据库连接的生成速度,来降低虚引用的数量,但是效果并不理想 。最终的解决方案是通过反射获取虚引用集合,利用定时任务来定期清理集合,避免 GC 处理虚引用耗时较长 。// 每两小时清理 connectionPhantomRefs,减少对 mixed GC 的影响SCHEDULED_EXECUTOR.scheduleAtFixedRate(() -> {try {Field connectionPhantomRefs = NonRegisteringDriver.class.getDeclaredField("connectionPhantomRefs");connectionPhantomRefs.setAccessible(true);Map map = (Map) connectionPhantomRefs.get(NonRegisteringDriver.class);if (map.size() > 50) {map.clear();}} catch (Exception e) {log.error("connectionPhantomRefs clear error!", e);}}, 2, 2, TimeUnit.HOURS);
利用定时任务清理虚引用效果立竿见影,每日几亿请求的服务 mixed GC 耗时只有 10 - 30 毫秒左右,系统也很稳定,线上运行将近一年没有任何问题 。优化——暴力破解到优雅配置最近又有同事遇到相同的问题,使用的 mysql-connector-java 版本与我们使用的版本一致,查看最新版本(8.0.32)的代码发现对数据库连接的虚引用有新的处理方式,不像老版本(5.1.38)中每一个连接都会生成虚引用,而是可以通过参数来控制是否需要生成 。类 AbandonedConnectionCleanupThread 的相关代码如下:
//静态变量通过 System.getProperty 获取配置private static boolean abandonedConnectionCleanupDisabled = Boolean.getBoolean("com.mysql.cj.disableAbandonedConnectionCleanup");public static boolean getBoolean(String name) {return parseBoolean(System.getProperty(name));}protected static void trackConnection(MysqlConnection conn,.NETworkResources io) {//判断配置的属性值来决定是否需要生成虚引用if (!abandonedConnectionCleanupDisabled) {···ConnectionFinalizerPhantomReference reference = new ConnectionFinalizerPhantomReference(conn, io, referenceQueue);connectionFinalizerPhantomRefs.add(reference);···}}
mysql-connector-java 的维护者应该是注意到了虚引用对 GC 的影响,所以优化了代码,让用户可以自定义虚引用的生成 。有了这个配置,就可以在启动参数上设置属性:
java -jar App.jar -Dcom.mysql.cj.disableAbandonedConnectionCleanup=true
或者在代码里设置属性:System.setProperty(PropertyDefinitions.SYSP_disableAbandonedConnectionCleanup,"true");
当 com.mysql.cj.disableAbandonedConnectionCleanup=true 时,生成数据库连接时就不会生成虚引用,对 GC 就没有任何影响了 。建议还是使用第一种方式,通过启动参数配置更灵活一点 。
什么是虚引用有些读者看到这里知道 mysql-connector-java 生成的虚引用对 GC 有一些副作用,但是还不太了解虚引用到底是什么,有什么作用,这里我们在虚引用上做一点点拓展 。
Java 虚引用(Phantom Reference)是Java中一种特殊的引用类型,它是最弱的一种引用 。与其他引用不同,虚引用并不会影响对象的生命周期,也不会影响对象的垃圾回收 。虚引用主要用于在对象被回收时收到系统通知,以便在回收时执行一些必要的清理工作 。
【MySQL 驱动中虚引用 GC 耗时优化与源码分析】上述虚引用的定义还是比较难理解,我们用代码来辅助理解:
先来生成一个虚引用:
//虚引用队列ReferenceQueue<Object> queue = new ReferenceQueue<>();//关联对象Object o = new Object();//调用构造方法生成一个虚引用 第一个参数就是关联对象 第二个参数是关联队列PhantomReference<Object> phantomReference = new PhantomReference<>(o, queue);//执行垃圾回收System.gc();//延时确保回收完毕Thread.sleep(100L);//当 Object o 被回收时可以从虚引用队列里获取到与之关联的虚引用 这里就是 phantomReference 这个对象Reference<?> poll = queue.poll();
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Mysql的存储引擎有哪些?
- ChatGPT驱动的虚拟试衣间,提升电子商务购物体验
- OpenHarmony 3.2 Release新特性解读之驱动HCS
- Redis 和 Mysql 如何保证数据一致性?
- MySQL索引,快速记忆法
- 带你读 MySQL 源码:Where 条件怎么过滤记录?
- 有MySQL了,为什么还硬要搭个MongoDB集群?
- MySQL Router高可用搭建,你学会了吗?
- 在 Meta 构建和部署 MySQL Raft
- mysql的3种安装方式及yum安装MySQL的步骤