【问题标题】:How to set max non-heap memory in a Java 8 (Spring Boot) application?如何在 Java 8(Spring Boot)应用程序中设置最大非堆内存?
【发布时间】:2020-10-13 17:10:06
【问题描述】:

我有 20 个 Spring Boot (2.3) 嵌入式 Tomcat 应用程序在 8GB 的​​ Linux 机器上运行。所有应用程序都是 Java 1.8 应用程序。机器内存不足,Linux 开始杀死我的一些应用程序进程。

使用 Linux top 和 Spring Boot admin,我注意到最大内存堆设置为 2GB:

java -XX:+PrintFlagsFinal -version | grep HeapSize

因此,20 个应用程序中的每一个都试图获得 2GB 的堆大小(物理内存的 1/4)。使用 Spring Boot admin 我可以看到只使用了 ~128 MB。所以我通过java -Xmx512m ... 将最大堆大小减少到 512 现在,Spring Boot 管理员显示:

1.33 GB 分配给非堆空间,但仅使用了 121 MB。为什么这么多分配给非堆空间?如何减少?

更新

根据顶部,每个 Java 进程占用大约 2.4GB (VIRT):

KiB Mem :  8177060 total,   347920 free,  7127736 used,   701404 buff/cache
KiB Swap:  1128444 total,  1119032 free,     9412 used.   848848 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
  2547 admin    20   0  2.418g 0.372g 0.012g S  0.0  4.8  27:14.43 java
  .
  .
  .

更新 2

我为其中一个进程运行了jcmd 7505 VM.native_memory,它报告了:

7505:

Native Memory Tracking:

Total: reserved=1438547KB, committed=296227KB
-                 Java Heap (reserved=524288KB, committed=123808KB)
                            (mmap: reserved=524288KB, committed=123808KB)

-                     Class (reserved=596663KB, committed=83423KB)
                            (classes #15363)
                            (malloc=2743KB #21177)
                            (mmap: reserved=593920KB, committed=80680KB)

-                    Thread (reserved=33210KB, committed=33210KB)
                            (thread #32)
                            (stack: reserved=31868KB, committed=31868KB)
                            (malloc=102KB #157)
                            (arena=1240KB #62)

-                      Code (reserved=254424KB, committed=27120KB)
                            (malloc=4824KB #8265)
                            (mmap: reserved=249600KB, committed=22296KB)

-                        GC (reserved=1742KB, committed=446KB)
                            (malloc=30KB #305)
                            (mmap: reserved=1712KB, committed=416KB)

-                  Compiler (reserved=1315KB, committed=1315KB)
                            (malloc=60KB #277)
                            (arena=1255KB #9)

-                  Internal (reserved=2695KB, committed=2695KB)
                            (malloc=2663KB #19903)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=20245KB, committed=20245KB)
                            (malloc=16817KB #167011)
                            (arena=3428KB #1)

-    Native Memory Tracking (reserved=3407KB, committed=3407KB)
                            (malloc=9KB #110)
                            (tracking overhead=3398KB)

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

【问题讨论】:

    标签: java spring-boot memory


    【解决方案1】:

    首先 - 不,1.33GB 没有分配。在屏幕截图中,您分配了 127MB 的非堆内存。 1.33GB 是最大限制。

    我看到您的元空间大约是 80MB,这应该不会造成问题。剩下的内存可以由很多东西组成。压缩类、代码缓存、本机缓冲区等......

    要获得消耗堆外内存的详细视图,您可以查询 MBean java.lang:type=MemoryPool,name=*,例如通过带有 MBean 插件的 VisualVM。

    但是,您的应用可能只是消耗了过多的本机内存。例如,许多来自 Netty 的 I/O 缓冲区可能是罪魁祸首(被 java.nio.DirectByteBuffer 用完)。如果这是罪魁祸首,您可以例如使用标志-Djdk.nio.maxCachedBufferSize 限制DirectByteBuffers 的缓存,或者使用-XX:MaxDirectMemorySize 设置限制。 对于究竟是什么在消耗您的 RAM 的确切答案,您必须创建一个堆转储并对其进行分析。

    所以回答您的问题“为什么分配给非堆空间这么多?如何减少?”没有很多分配给非堆空间。其中大部分是用于 I/O 和 JVM 内部的本机缓冲区。没有通用的开关或标志可以同时限制所有不同的缓存和池。

    现在要对房间里的大象讲话。我认为您的 真正 问题源于 RAM 很少。你说过你在 8GB 机器上运行 20 个 JVM 实例,限制为 512MB 的堆空间。这是不可持续的。 20 x 512MB = 10GB 堆,这超出了 8GB 总 RAM 所能容纳的容量。那是在您计算堆外/本机内存之前。您需要提供更多硬件资源、减少 JVM 数量或进一步减少堆/元空间和其他限制(我强烈建议不要这样做)。

    【讨论】:

    • 谢谢。这很有帮助。我仍然有一个疑问。根据顶部,我的每个 Java 进程的 VIRT 约为 2.4GB。如果只分配了 512 MB 堆空间 + 127 MB 非堆空间,那为什么 VIRT 会报告 2.5GB?
    • top 的VIRT 是虚拟内存man7.org/linux/man-pages/man1/top.1.html 它包括所有代码、数据和共享库以及已换出的页面和已映射但未使用的页面。所以这是很多事情的总和,我敢打赌其中大部分是在进程之间共享的。 RES(驻留内存)统计信息提供的信息要多得多——尽管仍然包含一些未使用的内存页面,但这些页面没有被回收。在您的情况下,它显示 0.372g 这不是一个巨大的数量。
    • 我以为 top 只显示了专门分配给一个进程的内存。我跑了jmcd(在我的OP中更新了信息)。它显示已提交 ~300MB 但保留 ~1.4GB。
    • 嗯,很好,这与我们目前看到的一致。 1.4GB 的保留,这基本上是整个内存的限制,其中提交/分配了 300MB 的空间。堆看起来和预期的一样——最大限制为 512MB,使用了 120MB。 80MB 的元空间(“类”统计数据)。 32MB 被线程堆栈占用,其余的甚至比这还要小。 300MB x 20 ~= 6GB,所以你运行的危险接近 RAM 限制(为操作系统和其他进程留下一些)。现在,如果一个或一些 JVM 上出现突然的负载,它很容易达到上限。
    • 谢谢小妖精。我在盒子里添加了 32GB 物理内存。现在,top 显示 3.3GB 可用空间。我期望比这更多的可用内存。我将每个 JVM 的堆空间保持在最大 512MB,所以不知道为什么只有 3.3GB 是可用的。
    【解决方案2】:

    除了已经说过的,here's 一篇非常好的文章,关于 JVM 中的 Metaspace,默认情况下保留大约 1GB(尽管它实际上可能不会使用那么多)。因此,如果您有许多小型应用程序并希望减少使用/保留的内存量,您可以使用标志 -XX:MaxMetaspaceSize 进行调整。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-27
      • 2021-05-23
      • 2017-02-23
      • 2017-08-03
      • 1970-01-01
      • 2023-04-03
      • 2020-05-22
      相关资源
      最近更新 更多