如何在Java 8(Spring Boot)应用程序中设置最大非堆内存?

huangapple go评论81阅读模式
英文:

How to set max non-heap memory in a Java 8 (Spring Boot) application?

问题

以下是您提供的翻译内容:

我在一台拥有8GB内存的Linux机器上运行了20个使用Spring Boot(2.3)嵌入式Tomcat的应用程序。所有的应用程序都是基于Java 1.8开发的。由于机器内存不足,Linux开始终止一些应用进程。

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

java -XX:+PrintFlagsFinal -version | grep HeapSize

因此,这20个应用程序中的每个都在尝试获取2GB的堆大小(占据物理内存的1/4)。使用Spring Boot Admin,我发现只有约128MB被使用。因此,我通过java -Xmx512m ...将最大堆大小减小到512MB。现在,Spring Boot Admin显示:

1.33GB被分配给非堆空间,但只有121MB被使用。为什么会分配这么多的非堆空间?如何减少这部分?

更新

根据top命令,每个Java进程占用约2.4GB(VIRT):

KiB Mem :  8177060 总计,   347920 空闲,  7127736 已用,   701404 buff/cache
KiB Swap:  1128444 总计,  1119032 空闲,     9412 已用,   848848 可用内存

  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)
英文:

I have 20 Spring Boot (2.3) embedded Tomcat applications running on a Linux machine with 8GB. All applications are Java 1.8 apps. The machine was running out of memory and Linux started killing some of my app processes as a result.

Using Linux top and Spring Boot admin, I noticed that the max memory heap was set to 2GB:

java -XX:+PrintFlagsFinal -version | grep HeapSize

As a result, each of the 20 apps are trying to get 2GB of heap size (1/4th of physical mem). Using Spring Boot admin I could see only ~128 MB is being used. So I reduced the max heap size to 512 via java -Xmx512m ... Now, Spring Boot admin shows:

如何在Java 8(Spring Boot)应用程序中设置最大非堆内存?

1.33 GB is allocated to non-heap space but only 121 MB is being used. Why is so much being allocated to non-heap space? How can I reduce?

Update

According to top each Java process is taking around 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
  .
  .
  .

Update 2

I ran jcmd 7505 VM.native_memory for one of the processes and it reported:

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)

答案1

得分: 2

首先,不,1.33GB并没有被分配出来。在截图中,你分配了127MB的非堆内存。1.33GB是最大限制。

我看到你的元空间大约是80MB,这不应该是一个问题。剩下的内存可能由许多因素组成,包括压缩类、代码缓存、本机缓冲区等等。

要详细查看是什么在占用堆外内存,你可以查询MBean java.lang:type=MemoryPool,name=*,例如通过带有MBean插件的VisualVM。

然而,你的应用程序可能只是简单地消耗了太多本机内存。例如,Netty的许多I/O缓冲区可能是罪魁祸首(被java.nio.DirectByteBuffer使用)。如果这是问题所在,你可以通过标志-Djdk.nio.maxCachedBufferSize限制DirectByteBuffer的缓存,或者使用-XX:MaxDirectMemorySize来设置限制。
要得到关于究竟是什么在占用你的RAM的明确答案,你需要创建一个堆转储文件并进行分析。

所以,回答你的问题,“为什么这么多内存被分配给非堆空间?如何减少?”实际上没有太多内存被分配给非堆空间。大部分是用于I/O的本机缓冲区和JVM内部。并没有通用的开关或标志可以一次性限制所有不同的缓存和池。

现在来解决问题的关键。我认为你真正的问题在于RAM非常少。你说你在8GB的机器上运行了20个只有512MB堆空间限制的JVM实例。这是不可持续的。20 x 512MB = 10GB的堆,这超过了8GB的总内存容量。而且这还没有计算堆外/本机内存。你需要提供更多的硬件资源,减少JVM数量,或进一步降低堆/元空间和其他限制(我强烈建议不要这样做)。

英文:

First of all - no, 1.33GB is not allocated. On the screenshot you have 127MB of nonheap memory allocated. The 1.33GB is the max limit.

I see your metaspace is about 80MB which should not pose a problem. The rest of the memory can be composed by a lot of things. Compressed classes, code cache, native buffers etc...

To get the detailed view of what is eating up the offheap memory, you can query the MBean java.lang:type=MemoryPool,name=*, for example via VisualVM with an MBean plugin.

However, your apps may simply be eating too much native memory. For example many I/O buffers from Netty may be the culprit (used up by the java.nio.DirectByteBuffer). If that's the culprit, you can for example limit the caching of the DirectByteBuffers with the flag -Djdk.nio.maxCachedBufferSize, or place a limit with -XX:MaxDirectMemorySize.
For a definitive answer of what exactly is eating your RAM, you'd have to create a heap dump and analyze it.

So to answer your question "Why is so much being allocated to non-heap space? How can I reduce?" There's not a lot allocated to non-heap space. Most of it is native buffers for I/O, and JVM internals. There is no universal switch or flag to limit all the different caches and pools at once.

Now to adress the elephant in the room. I think your real issue stems from simply having very little RAM. You've said you are running 20 instances of JVM limited to 512MB of heap space on 8GB machine. That is unsustainable. 20 x 512MB = 10GB of heap, which is more than you can accommodate with 8GB of total RAM. And that is before you even count in the off-heap/native memory. You need to either provide more HW resources, decrease the JVM count or further decrease the heap/metaspace and other limits (which I strongly advise not to).

答案2

得分: 0

除了已经提到的内容,这里 有一篇非常好的关于JVM中Metaspace的文章,默认情况下会保留约1GB的空间(尽管实际使用可能没有那么多)。因此,如果您有许多小型应用程序并且希望减少内存使用/保留的数量,这是另一个可以使用标志-XX:MaxMetaspaceSize来进行调整的地方。

英文:

In addition to what has already been stated, here's a very good article about the Metaspace in the JVM which by defaults reserves about 1GB (though it may not actually use that much). So that's another thing you can tune using the flag -XX:MaxMetaspaceSize if you have many small apps and want to decrease the amount of memory used/reserved.

huangapple
  • 本文由 发表于 2020年10月14日 01:10:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/64339950.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定