You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java进程(Jetty WebSocket)驻留内存过高原因排查求助

为什么Java进程的RSS远高于你计算的内存总和?

这个问题我之前帮团队排查过类似的,你的计算思路没问题,但确实漏了很多容易被忽略的内存开销——不管是JVM内部的还是操作系统层面的,我来给你逐一梳理:

  • JVM堆外的直接内存(Direct ByteBuffer)
    这绝对是最常见的“隐形内存”,尤其是像Jetty WebSocket这种大量用NIO的应用。Direct ByteBuffer是直接在操作系统的堆外分配内存,完全不属于JVM堆的统计范畴(不会计入你说的231MB堆Committed值),但会实实在在占用物理内存,反映在RSS里。WebSocket处理网络数据时会频繁用到直接内存做缓冲区,这部分很可能占了不小的比例。

  • JVM内部的其他隐藏开销
    除了堆和非堆,JVM自身还有不少内存消耗:

    • JIT编译的代码缓存:比如HotSpot的Code Cache,用来存储编译后的native代码,这部分属于非堆但有时候没被完全统计到;
    • 类加载器的辅助结构、GC的内部数据(比如标记栈、回收队列)、线程的本地存储(TLS)等,这些都是JVM运行必需的内存,但不会被堆或非堆的常规统计覆盖。
  • 操作系统层面的进程开销
    RSS是操作系统统计的进程总驻留内存,包含很多JVM之外的部分:

    • 共享库/动态链接库:JVM本身依赖的libjvm.so、libc.so等系统库,还有你应用用到的native扩展(比如Jetty的native优化库、加密库),这些库加载后会占用物理内存,虽然部分是多进程共享的,但操作系统还是会把它们计入单个进程的RSS(不同OS统计逻辑略有差异);
    • 页表(Page Tables):每个线程都需要页表来映射虚拟内存到物理内存,151个线程的页表加起来也是一笔不小的开销,线程越多这部分占比越大;
    • 文件缓存与内核态内存:文件描述符本身占用的内存很小,但如果通过这些fd打开了大文件并被操作系统缓存,或者进程的内核栈、信号处理结构等,都会被计入RSS。
  • 第三方库的native内存分配
    很多Java库底层会调用native代码分配堆外内存:

    • Jetty的WebSocket底层实现会用native缓冲处理网络IO;
    • 日志框架(比如Logback)如果用了native的磁盘缓冲;
    • 加密、压缩类库的native实现,都会在JVM堆外分配内存,这些都不会被你的堆/非堆统计覆盖。
  • 内存对齐与碎片化
    操作系统和JVM分配内存都是以页为单位(通常4KB),内存块需要对齐到页边界,这会产生一些碎片内存。比如一个实际占用101KB的对象,会被分配104KB的物理内存,大量这样的碎片累积起来,也会让RSS比你计算的总和高不少。

如果你想验证这些部分,可以用这些工具:

  • 用jmap -directmemory <pid>查看直接内存的使用量;
  • 用pmap -x <pid>打印进程的内存映射详情,能看到各个共享库、堆外内存区域的具体占用;
  • 用jcmd <pid> VM.native_memory(需要开启-XX:NativeMemoryTracking=summary)来查看JVM的native内存明细。

内容的提问来源于stack exchange,提问作者Abhishek Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:19:21