Postgres JDBC LazyCleaner 是否会导致类加载器内存泄漏?

问题描述 投票:0回答:1

我有一个 Spring Boot Web 应用程序,每天部署多次,因此干净的取消部署至关重要。 Tomcat 报告内存泄漏,MAT 向我指出

我正在使用 postgresql-42.6.0.jar,并将该 JAR 放置在 Tomcat 10.1 ./lib 目录中(据我所知,JDBC 驱动程序应该如此)。在我看来,Tomcat 启动时出现的 Web 应用程序会加载

org.postgresql.util.LazyCleaner
,然后启动“PostgreSQL-JDBC-Cleaner”线程。由于线程继承了由 webapp 类加载器加载的 ProtectionDomain 的 AccessControlContext,因此该类加载器将永远拥有一个无法进行 gc 的引用,从而导致内存泄漏。

我在网上找不到任何其他人也遇到此问题的信息。我是不是错过了什么?

java postgresql tomcat jdbc memory-leaks
1个回答
0
投票

LazyCleaner 是在 42.6.0 中引入的,作为解决内存泄漏的尝试(https://github.com/pgjdbc/pgjdbc/issues/1360https://github.com/pgjdbc/pgjdbc/issues /1431)。该类使用 Java 的 PhantomReference 来查找“引用”对象何时被 gc'ed,然后运行关联的“CleaningAction”。所有这些都在“PostgreSQL-JDBC-Cleaner”线程中运行。如果线程在 TTL(默认为 30 秒)内根本没有任何“引用”对象,则线程退出。如有必要,注册任何“引用”对象都会创建一个新线程。

postgresql-42.6.0.jar 的哪些部分使用了 LazyCleaner?

PgConnection.java
cleanable = LazyCleaner.getInstance().register(leakHandle, finalizeAction);

StreamWrapper.java
cleaner = LazyCleaner.getInstance().register(leakHandle, tempFileHolder);

SharedTimer.java
this.timerCleanup = LazyCleaner.getInstance().register(refCount, new TimerCleanup(timer));

所以是:

  • 每个 PgConnection 都会向 PgConnectionCleaningAction 注册自己
  • 每个 psql StreamWrapper 都会向 TempFileHolder 注册自身(实现 CleaningAction)以关闭流并删除临时文件
  • 每个 psql SharedTimer 都会向 TimerCleanup 注册自己以取消计时器

后两者是来来回回的注册,但 PgConnection 是一个问题。在设置中

  • Tomcat 与 /lib 中的 postgresql-42.6.0.jar 用于通用类加载器
  • 使用 PSQL 的多个 Web 应用程序
  • 每个 Web 应用程序都使用连接池,使连接保持活动一段时间

每个 Web 应用程序都会在 LazyCleaner 中注册 PgConnection 实例,该实例作为公共库共享(按设计)。因此,重新部署一个 Web 应用程序不会停止 LazyCleaner 线程,因为其他 Web 应用程序仍然注册了 PgConnections。

线程在启动时将通过其堆栈跟踪继承 AccessControlContext,其效果是初始 LazyCleaner 线程将保存对最初启动该线程的 Web 应用程序的 ProtectionDomain 的引用。如上所述,LazyCleaner 线程永远不会退出,从而使该 webapp 类加载器免受 gc 的影响。

目前,降级到 postgresql-42.5.6.jar 可以解决该问题。

© www.soinside.com 2019 - 2024. All rights reserved.