【问题标题】:Java consumes memory more than Xmx argumentJava 消耗的内存超过 Xmx 参数
【发布时间】:2018-07-25 15:58:50
【问题描述】:

我有一个非常简单的 Web 服务器类(基于 Java SE 的 HttpServer 类)。

当我使用此命令启动编译类以限制内存使用时:

java -Xmx5m -Xss5m -Xrs -Xint -Xbatch Test

现在,如果我使用 top 命令检查内存,则表明执行我的类的 Java 进程使用了​​大约 31MB 的常驻内存。

我想知道这 30MB 的用途是什么?

【问题讨论】:

  • 仅供参考 5,按现代标准,Mega字节只是很小的内存量。整个 JVM 进程的 30MB 也是如此。此外,-Xmx 选项仅指定最大 heap 大小。 JVM 也占用空间。
  • -Xmx 参数指定 heap 的最大大小,而不是 JVM 使用的总内存。
  • 你给每个线程一个5m的栈大小。问题应该是为什么它使用31m内存……

标签: java jvm


【解决方案1】:

正如 cmets 和答案所暗示的,还有许多其他因素需要考虑 测量 JVM 内存使用时的帐户。但是,我认为没有任何答案进入 几乎足够的深度。

JVM 内存概览

让我们来问“我想知道这 30MB 是用来做什么的?”脑袋上。为此,这里有一个简单的 java 类:

// HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello world!");
        Thread.sleep(10000); // wait 10 seconds so we can get memory usage
    }
}

现在用堆约束编译和运行它:

$ nohup java -Xms2m -Xmx2m HelloWorld & # run in background
$ ps aux | awk 'NR==1; /[H]elloWorld/'
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
chaospie  6204  6.0  0.1 2662860 23040 pts/2   Sl   19:15   0:00 java -Xms2m -Xmx2m HelloWorld

查看上面的 RSS(驻留集大小,或该进程正在使用多少内存),我们看到 JVM 的 进程正在使用大约 23MB 的内存。要了解原因,让我们做一些分析。获得好成绩的最快方法 概述是打开NativeMemorytracking 使用jcmd 工具的VM.native_memory 命令。所以, 让我们再次运行我们的应用程序:

$ nohup java -XX:NativeMemoryTracking=summary -Xms2M -Xmx2M HelloWorld &
[2] 6661
nohup: ignoring input and appending output to 'nohup.out'

$ ps aux | awk 'NR==1; /[H]elloWorld/'
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
chaospie  6661  5.0  0.1 2662860 23104 pts/2   Sl   19:21   0:00 java -XX:NativeMemoryTracking=summary -Xms2M -Xmx2M HelloWorld

$ jcmd 6661 VM.native_memory summary
6661:

Native Memory Tracking:

Total: reserved=1360145KB, committed=61177KB
-                 Java Heap (reserved=2048KB, committed=2048KB)
                            (mmap: reserved=2048KB, committed=2048KB)

-                     Class (reserved=1066093KB, committed=14189KB)
                            (classes #402)
                            (malloc=9325KB #146)
                            (mmap: reserved=1056768KB, committed=4864KB)

-                    Thread (reserved=20646KB, committed=20646KB)
                            (thread #21)
                            (stack: reserved=20560KB, committed=20560KB)
                            (malloc=62KB #110)
                            (arena=23KB #40)

-                      Code (reserved=249632KB, committed=2568KB)
                            (malloc=32KB #299)
                            (mmap: reserved=249600KB, committed=2536KB)

-                        GC (reserved=10467KB, committed=10467KB)
                            (malloc=10383KB #129)
                            (mmap: reserved=84KB, committed=84KB)

-                  Compiler (reserved=132KB, committed=132KB)
                            (malloc=1KB #21)
                            (arena=131KB #3)

-                  Internal (reserved=9453KB, committed=9453KB)
                            (malloc=9421KB #1402)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=1358KB, committed=1358KB)
                            (malloc=902KB #86)
                            (arena=456KB #1)

-    Native Memory Tracking (reserved=143KB, committed=143KB)
                            (malloc=86KB #1363)
                            (tracking overhead=57KB)

-               Arena Chunk (reserved=175KB, committed=175KB)
                            (malloc=175KB)

内存区域

让我们分解一下1

  • Java 堆:这是堆 -
  • Class :这是 Metaspace,假设您使用的是 java 8。
  • Thread :这显示了线程的数量,以及线程的整体内存使用情况(注意,本节中使用的stack反映了Xss的值乘以线程数,你可以得到默认 -Xssvalue 和 java -XX:+PrintFlagsFinal -version |grep ThreadStackSize)。
  • 代码 :代码缓存 - JIT(即时编译器)使用它来缓存已编译的代码。
  • GC : 垃圾收集器使用的空间。
  • 编译器 : JIT 在生成代码时使用的空间。
  • 符号:这是用于符号、字段名称、方法签名等...
  • Native Memory Tracking :本机内存跟踪器本身使用的内存。
  • Arena Chunk :这与 malloc arenas 2 相关。

不仅仅是堆!

保留、承诺和 RSS

请注意,每个区域都有一个 committed 和一个 reserved 部分。为了保持简短 reserved 是它可以增长的东西,committed 是目前承诺使用的东西。 例如参见Java Heap 部分:Java Heap (reserved=2048KB, committed=2048KB)reserved 是我们的 -Xmx value 和committed 将是我们的-Xms value,在这种情况下它们是相等的。

还要注意committed 的总大小 - 它没有 反映 RSS 报告的实际使用情况(或top 中的 RES 列)。它们不同的原因是 RSS 显示 在物理内存中已经和仍在使用的所有内存页面的大小,而committed 显示的内存是 使用的包括不在物理内存中的内容3.

这还有很多,但是 JVM 和 OS 内存管理是一个复杂的话题,所以我希望这至少能在高层次上回答你的问题。


  1. https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr022.html
  2. 来自 JVM 本机内存跟踪文档 (https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html#BABJGHDB):

Arena 是使用 malloc 分配的一块内存。当退出作用域或离开代码区域时,内存会从这些块中批量释放。这些块可以在其他子系统中重用以保存临时内存,例如线程前分配。 Arena malloc 策略确保没有内存泄漏。因此 Arena 是作为一个整体而不是单个对象进行跟踪的。无法跟踪某些初始内存量。

  1. 这里要讨论 RSS、保留和提交内存之间的区别就太多了,操作系统内存管理是一个复杂的话题,但是请参阅 this answer 以获得良好的概述。

【讨论】:

    【解决方案2】:

    Java 在虚拟机上运行,​​而不是直接在您的硬件上运行。这意味着这台机器需要自己的内存才能运行。您允许程序使用的 5MB 可能意味着 Java 虚拟机 (JVM) 正在使用另外 26MB。

    【讨论】:

      【解决方案3】:

      -Xmx5m 仅用于堆内存,top 会显示整个内存,包括进程内处理的 JNI 调用使用的本机内存。

      【讨论】:

        猜你喜欢
        • 2017-06-19
        • 1970-01-01
        • 2011-12-21
        • 2021-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多