我有一个在Linux下运行的Java应用程序的问题。

当我使用默认的最大堆大小(64 MB)启动应用程序时,我看到使用tops应用程序为应用程序分配了240 MB的虚拟内存。这就给计算机上的其他一些软件带来了一些问题,这些软件的资源相对有限。

保留的虚拟内存无论如何都不会被使用,据我所知,因为一旦达到堆限制,就会抛出OutOfMemoryError错误。我在windows下运行相同的应用程序,我看到虚拟内存大小和堆大小是相似的。

是否有任何方式,我可以配置在Linux下的Java进程使用的虚拟内存?

编辑1:问题不在于堆。问题是,如果我设置一个128 MB的堆,Linux仍然分配210 MB的虚拟内存,这是不需要的,永远。**

编辑2:使用ulimit -v允许限制虚拟内存的数量。如果大小集低于204 MB,那么即使应用程序不需要204 MB,只需要64 MB,它也不会运行。所以我想了解为什么Java需要这么多虚拟内存。这种情况可以改变吗?

编辑3:系统中还运行着其他几个嵌入式应用程序。系统确实有一个虚拟内存限制(来自评论,重要细节)。


当前回答

Java和glibc >= 2.10(包括Ubuntu >= 10.04, RHEL >= 6)有一个已知的问题。

解药是把这个环境。变量:

export MALLOC_ARENA_MAX=4

如果您正在运行Tomcat,您可以将此添加到TOMCAT_HOME/bin/setenv.sh文件中。

对于Docker,将此添加到Dockerfile

ENV MALLOC_ARENA_MAX=4

IBM有一篇关于设置MALLOC_ARENA_MAX的文章 https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en

这篇博文说

常驻记忆以一种类似于 内存泄漏或内存碎片。

还有一个开放的JDK bug JDK-8193521“glibc默认配置浪费内存”

在谷歌或SO上搜索MALLOC_ARENA_MAX以获得更多参考。

你可能还想调优其他malloc选项,以优化低内存碎片分配:

# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536

其他回答

Sun JVM需要大量内存用于HotSpot,并且它将运行时库映射到共享内存中。

如果内存是个问题,可以考虑使用另一个适合嵌入的JVM。IBM有j9,还有使用GNU类路径库的开源“jamvm”。此外,Sun在sun黑子上运行了Squeak JVM,所以有替代方案。

不能配置虚拟机所需内存数量。但是,请注意,这是虚拟内存,而不是常驻内存,因此如果不实际使用,它只会保持在那里而不会受到损害。

提醒一下,您可以尝试其他JVM,而不是Sun JVM,内存占用更小,但我不能在这里建议。

Sun的java 1.4有以下参数来控制内存大小:

-Xmsn Specify the initial size, in bytes, of the memory allocation pool. This value must be a multiple of 1024 greater than 1MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is 2MB. Examples: -Xms6291456 -Xms6144k -Xms6m -Xmxn Specify the maximum size, in bytes, of the memory allocation pool. This value must a multiple of 1024 greater than 2MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value is 64MB. Examples: -Xmx83886080 -Xmx81920k -Xmx80m

http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html

Java 5和6有更多的功能。参见http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp

只是一个想法,但是您可以检查ulimit -v选项的影响。

这不是一个实际的解决方案,因为它将限制所有进程可用的地址空间,但这将允许您使用有限的虚拟内存检查应用程序的行为。

分配给Java进程的内存量与我所期望的相当。我在嵌入式/内存有限的系统上运行Java时也遇到过类似的问题。运行任何具有任意VM限制的应用程序,或者在没有足够交换量的系统上运行应用程序,都容易崩溃。这似乎是许多现代应用程序的本质,它们的设计并不适合在资源有限的系统上使用。

您还可以尝试其他一些选项来限制JVM的内存占用。这可能会减少虚拟内存占用:

- xx:ReservedCodeCacheSize=32m保留代码缓存大小(单位:字节)- maximum 代码缓存大小。(Solaris 64位, -server x86: 48m;在 1.5.0_06及以前版本,Solaris 64位和and64: 1024m。 -XX:MaxPermSize=64m永久代的大小。[5.0及更新版本: 64位虚拟机扩展30%;1.4 amd64: 96;1.3.1 -client: 32m.]

此外,还应该将-Xmx(最大堆大小)设置为尽可能接近应用程序实际内存使用峰值的值。我相信JVM的默认行为仍然是每次将堆大小扩展到最大时将堆大小增加一倍。如果你从32M的堆开始,你的应用程序的峰值是65M,那么堆最终会增长32M -> 64M -> 128M。

你也可以尝试这样做,让VM不那么积极地增长堆:

-XX:MinHeapFreeRatio=40垃圾回收后的最小堆空闲百分比 避免扩张。

另外,根据我几年前的实验,加载的本机库的数量对最小内存占用有很大的影响。如果我没记错的话(我可能记错了),加载java.net.Socket增加了超过15M。