Is there an existing issue for this?
Current Behavior
导出hertzbeat dump文件,分析如下:
📊 Java内存占用分析报告
| 类名 |
实例数量 |
自身大小 |
总大小 |
byte[] |
396,159 |
49.56 MB |
>= 49.56 MB |
org.postgresql.jdbc.PgConnection |
819 |
0.10 MB |
>= 38.84 MB |
java.lang.ref.Finalizer |
1,149 |
0.04 MB |
>= 38.41 MB |
java.lang.Object[] |
59,737 |
6.01 MB |
>= 30.34 MB |
java.util.concurrent.ConcurrentHashMap |
5,735 |
0.35 MB |
>= 26.61 MB |
java.util.concurrent.ConcurrentHashMap$Node[] |
3,296 |
1.54 MB |
>= 26.31 MB |
java.lang.String |
377,327 |
8.64 MB |
>= 25.11 MB |
java.util.HashMap$Node[] |
41,292 |
5.56 MB |
>= 24.98 MB |
java.util.HashMap |
22,951 |
1.05 MB |
>= 24.38 MB |
java.util.concurrent.ConcurrentHashMap$Node |
125,723 |
3.84 MB |
>= 23.15 MB |
java.util.ArrayList |
29,540 |
0.68 MB |
>= 22.96 MB |
java.util.HashMap$Node |
467,433 |
14.26 MB |
>= 19.48 MB |
org.postgresql.core.v3.QueryExecutorImpl |
819 |
0.17 MB |
>= 18.77 MB |
org.postgresql.jdbc.TypeInfoCache |
819 |
0.07 MB |
>= 18.36 MB |
java.util.zip.ZipFile$Source |
437 |
0.03 MB |
>= 17.49 MB |
📝 表格说明
- 类名:Java类的全限定名
- 实例数量:该类在内存中的实例个数
- 自身大小:所有实例本身占用的内存大小(不包括引用的对象)
- 总大小:所有实例及其引用对象占用的总内存大小
PgConnection 的对象高达819个,占用38MB的空间,随着时间的增长还会越来越多
查看持有PgConnection对象的引用如下:
📊 PostgreSQL连接对象内存占用分析
| Class Name |
Objects |
Shallow Heap |
| org.postgresql.jdbc.PgConnection |
819 |
0.10 MB |
| ├─ org.postgresql.jdbc.TypeInfoCache |
819 |
0.07 MB |
| ├─ org.postgresql.jdbc.PgPreparedStatement |
457 |
0.05 MB |
| ├─ java.lang.ref.Finalizer |
819 |
0.03 MB |
| └─ com.zaxxer.hikari.pool.PoolEntry |
10 |
0.00 MB |
| Total |
4 entries |
|
从上可以看出,819个PgConnection处于未关闭,对象一直未释放
📊 问题定位
JdbcCommonCollect类中,getConnection方法
Connection connection = DriverManager.getConnection(url, username, password);
三条泄漏路径
1.高并发下的竞争条件(主要原因):多个采集线程同时对同一目标发起 getCache() → 都 miss → 各自创建新连接 → 只有最后一个进缓存,其余的连接永远不会被关闭
2.reuseConnection=true + 异常路径:DriverManager.getConnection() 部分建立连接后抛出异常,连接对象已创建但 connection 变量从未赋值,无法 close
3.缓存 key 失效后的连接泄漏:缓存 600 秒超时后,cleanTimeoutOrUnHealthyCache 移除旧连接,但在移除和重建之间的并发窗口内可能产生孤儿连接
修改方案
改为 per-target HikariCP DataSource:把 GlobalConnectionCache 中存的 JdbcConnect(Connection) 改为存 HikariDataSource,从池中借还,彻底绑回 Hikari 管理
经过上述修改,PgConnection连接数得到有效控制,内存不在疯长
Expected Behavior
No response
Steps To Reproduce
No response
Environment
HertzBeat version(s):1.8.0
Debug logs
No response
Anything else?
No response
Is there an existing issue for this?
Current Behavior
导出hertzbeat dump文件,分析如下:
📊 Java内存占用分析报告
byte[]org.postgresql.jdbc.PgConnectionjava.lang.ref.Finalizerjava.lang.Object[]java.util.concurrent.ConcurrentHashMapjava.util.concurrent.ConcurrentHashMap$Node[]java.lang.Stringjava.util.HashMap$Node[]java.util.HashMapjava.util.concurrent.ConcurrentHashMap$Nodejava.util.ArrayListjava.util.HashMap$Nodeorg.postgresql.core.v3.QueryExecutorImplorg.postgresql.jdbc.TypeInfoCachejava.util.zip.ZipFile$Source📝 表格说明
PgConnection 的对象高达819个,占用38MB的空间,随着时间的增长还会越来越多
查看持有PgConnection对象的引用如下:
📊 PostgreSQL连接对象内存占用分析
从上可以看出,819个PgConnection处于未关闭,对象一直未释放
📊 问题定位
JdbcCommonCollect类中,getConnection方法
三条泄漏路径
1.高并发下的竞争条件(主要原因):多个采集线程同时对同一目标发起 getCache() → 都 miss → 各自创建新连接 → 只有最后一个进缓存,其余的连接永远不会被关闭
2.reuseConnection=true + 异常路径:DriverManager.getConnection() 部分建立连接后抛出异常,连接对象已创建但 connection 变量从未赋值,无法 close
3.缓存 key 失效后的连接泄漏:缓存 600 秒超时后,cleanTimeoutOrUnHealthyCache 移除旧连接,但在移除和重建之间的并发窗口内可能产生孤儿连接
修改方案
改为 per-target HikariCP DataSource:把 GlobalConnectionCache 中存的 JdbcConnect(Connection) 改为存 HikariDataSource,从池中借还,彻底绑回 Hikari 管理
经过上述修改,PgConnection连接数得到有效控制,内存不在疯长
Expected Behavior
No response
Steps To Reproduce
No response
Environment
Debug logs
No response
Anything else?
No response