Java 进程内存无限增长。内存泄漏?

2022-09-04 23:17:52

我们有一个在 Solaris 10 上运行的 java 进程,为大约 200-300 个并发用户提供服务。管理员报告说,进程使用的内存会随着时间的推移而显著增加。它在几天内达到2GB,并且从未停止增长。

我们已经转储了堆并使用Eclipse Memory Profiler对其进行了分析,但无法看到任何异常情况。堆大小非常小。

添加内存统计日志记录后,在我们的应用程序中,我们发现管理员使用的“top”实用程序报告的内存使用情况与MemoryMXBean和运行时库报告的使用情况之间存在差异。

下面是两者的输出。

Memory usage information 

From the Runtime library
Free memory: 381MB
Allocated memory: 74MB
Max memory: 456MB
Total free memory: 381MB

From the MemoryMXBean library.
Heap Committed: 136MB
Heap Init: 64MB
Heap Used: 74MB
Heap Max: 456MB
Non Heap Committed: 73MB
Non Heap Init: 4MB
Non Heap Used: 72MB

Current idle threads: 4
Current total threads: 13
Current busy threads: 9
Current queue size: 0
Max threads: 200
Min threads: 8
Idle Timeout: 60000

  PID USERNAME NLWP PRI NICE  SIZE   RES STATE    TIME   CPU COMMAND
99802 axuser   115   59    0 2037M 1471M sleep  503:46 0.14% java

这怎么可能?top 命令报告了更多的使用情况。我期望RES应该接近堆+非堆。

但是,pmap -x 报告堆中的大部分内存:

Address     Kbytes       RSS       Anon     Locked Mode   Mapped File
*102000         56         56         56       - rwx----    [ heap ]
*110000       3008       3008       2752       - rwx----    [ heap ]
*400000    1622016    1621056    1167568       - rwx----    [ heap ]
*000000      45056      45056      45056       - rw-----    [ anon ]

任何人都可以对此进行一些阐明吗?我完全迷失了。

谢谢。

更新

这在 Linux 上似乎不是问题。

此外,根据Peter Lawrey的回应,pmap报告的“堆”是原生堆而不是Java堆。


答案 1

我遇到了类似的问题,并找到了解决方案:

Solaris 11
JDK10
REST application using HTTPS (jetty server)
There was a significant increase of c-heap (observed via pmap) over time

我决定用libumem做一些压力测试。所以我开始了这个过程

UMEM_DEBUG=默认UMEM_LOGGING=事务LD_PRELOAD=libumem.so.1

并用https请求强调应用程序。过了一会儿,我用mdb连接到了这个过程。在 mdb 中,我使用了命令 ::findleaks,它显示为泄漏:

libucrypto.so.1`ucrypto_digest_init

因此,似乎JCA(Java Cryptography Architecture)实现OracleUcrypto在Solaris上存在一些问题。

通过更新$JAVA_HOME/conf/security/java.security文件解决了这个问题 - 我将OracleUcrypto的优先级更改为3,将SUN实现的优先级更改为1

security.provider.3=OracleUcrypto
security.provider.2=SunPKCS11 ${java.home}/conf/security/sunpkcs11-solaris.cfg
security.provider.1=SUN

在此之后,问题消失了。

这也解释了为什么在linux上没有问题 - 因为JCA提供商有不同的含义。


答案 2

在垃圾回收环境中,保留未使用的指针相当于“泄漏失败”,并阻止 GC 完成其工作。不小心把指针放在周围真的很容易。

一个常见的罪魁祸首是哈希表。另一个是数组或向量,它们在逻辑上被清除(通过将重用索引设置为0),但数组的实际内容(在use索引上方)仍然指向某些东西。


推荐