Postgres JDBC的LazyCleaner是否会导致类加载器内存泄漏?
PostgreSQL JDBC驱动引发Tomcat类加载器内存泄漏的排查方案
问题本质
你将postgresql-42.6.0.jar放在Tomcat 10.1的./lib目录(符合JDBC驱动部署规范),但实际Web应用的类加载器加载了org.postgresql.util.LazyCleaner,该类启动的PostgreSQL-JDBC-Cleaner线程继承了关联Web应用类加载器的AccessControlContext,导致类加载器被永久引用无法GC,最终触发内存泄漏。
排查方向(你可能遗漏的点)
1. 驱动重复打包
检查你的Spring Boot应用包的WEB-INF/lib目录,确认是否存在postgresql-*.jar。如果有,Web应用类加载器会优先加载本地依赖,而非Tomcat公共类加载器中的驱动。解决方式:
- Maven中给PostgreSQL依赖添加
<scope>provided</scope>,避免打包进应用:<dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.6.0</version> <scope>provided</scope> </dependency> - Gradle中将
implementation 'org.postgresql:postgresql:42.6.0'改为compileOnly 'org.postgresql:postgresql:42.6.0'。
2. Tomcat类加载器配置异常
- 检查
conf/catalina.properties的common.loader配置,确认${catalina.base}/lib和${catalina.home}/lib在加载路径中(默认包含,若被修改会导致公共类加载器无法加载驱动)。 - 检查Web应用的
META-INF/context.xml或Tomcat全局conf/context.xml,是否存在强制指定应用类加载器加载驱动的配置。
3. 驱动类加载逻辑验证
org.postgresql.util.LazyCleaner的静态代码块会在类加载时启动清理线程。如果该类由Tomcat公共类加载器加载,线程关联的是公共类加载器,不会影响Web应用卸载;只有当类被应用类加载器加载时才会出问题。
- 部署后用
jstack查看PostgreSQL-JDBC-Cleaner线程的上下文类加载器:
正常输出应显示jstack <tomcat-pid> | grep -A 5 "PostgreSQL-JDBC-Cleaner"org.apache.catalina.loader.StandardClassLoader类加载器,而非Web应用类加载器(如org.apache.catalina.loader.ParallelWebappClassLoader)。
验证步骤
- 移除应用包中的PostgreSQL驱动,仅保留Tomcat
./lib下的版本。 - 重启Tomcat,部署应用后检查线程的上下文类加载器。
- 多次部署/卸载应用,通过MAT确认Web应用类加载器是否能被GC回收。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

