java process consume more memory than Xmx

java应用程序启动时,我们常常会加上 -Xmx 选项,以确保进程不会吃掉我们限制的范围。

然后你可能收到这样的惊喜。当打开命令行工具,查看 top 信息。会发现应用吃掉的内存[resident memory usage(RES)]超过了我们的预设。

为什么进程消耗的内存超过了我们分配?是bug还是完全正常的现象?

首先,一部分原因可能是代码造成的内存泄漏。但是,99%的情况下,都是jvm的正常行为。因为 -Xmx 仅仅是限制了应用程序的堆大小。

除了堆,这里还有其他几个内存空间被应用程序使用,比如 permgenstack 。为了限制它们,我们需要额外指定几个参数 -XX:MaxPermSize-Xss

简而言之,我们可以通过如下公式,预测我们应用的内存占用:

1
Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss]

但是,除应用外。jvm本身也要消耗内存:

  • 垃圾收集
    • java是一门自动垃圾回收语言,自动回收程序跟踪对象,执行回收需要占用内存
    • G1更是出了名的倾向于过多占用内存
  • 即时编译(JIT)
    • 虚拟机为了提升代码执行效率
    • 需要跟踪代码执行
  • 堆外内存
    • 使用直接或映射的 ByteBuffers 或者三方工具。在无意中拓展了堆空间,这些空间不被java虚拟机控制
  • 本地接口(JNI)
    • 使用本地代码,就要占用本地内存
  • 元空间(Metaspace)
    • java8中新增的空间,取代永久代保存类定义信息。