Java使用的内存比-Xmx分配的内存要多得多

31

我正在为一个Java课程写项目,教授说我们不能使用超过200m的内存,所以我用-Xmx50m将堆栈内存限制为50m(只是为了确保),但是根据top的显示,它仍在使用300m。

我尝试运行Eclipse Memory Analyzer,报告只有26m。

所有这些都可能是堆栈内存吗?我很确定我从来没有超过大约300个方法调用深度(是的,这是一个递归的DFS搜索),所以这意味着每个堆栈帧几乎使用了1兆字节,这似乎难以置信。

该程序是单线程的。是否有人知道我可以减少内存使用的其他地方?另外,如何检查/限制堆栈使用多少内存?

更新:我现在正在使用以下JVM选项,但没有效果(根据top仍然是大约300m):-Xss104k -Xms40m -Xmx40m -XX:MaxPermSize=1k

另一个更新:实际上,如果我让它运行更长的时间(使用所有这些选项),约一半的时间它会在4或5秒钟后突然下降到150m(而另一半则不会下降)。这使得情况非常奇怪,因为我的程序没有随机性(并且如我所说,它是单线程的),所以没有理由它在不同的运行中表现不同。

这可能与我使用的JVM有关吗?

java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.3) (6b27-1.12.3-0ubuntu1~10.04)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)

根据java -h的说明,默认JVM为-server。我尝试添加-cacao选项,现在(加上所有其他选项)它只有59m。所以我想这解决了我的问题。有人能解释一下为什么需要这样吗?还有,有任何缺点需要我知道的吗?

再更新一下:与server相比,cacao真的非常慢。这是一个糟糕的选项。


堆内存和栈内存是分别配置的。在 top 中看到的内存包括两者。 - dnault
我知道,但我不认为这是堆栈内存。我没有使用那么多。那么这些内存从哪里来呢? - dspyz
你是否也设置了最小堆大小?如果 -Xms 大于 -Xmx,我认为它会使用最大值 -Xms - Sotirios Delimanolis
不使用-server不会改变任何东西。 - dspyz
但这也包括共享库的大小等。如果您的Prof允许200m,则我会将其解释为-Xmx200m,没有更多,也没有更少。 - Ingo
显示剩余5条评论
5个回答

21

Top命令反映了Java应用程序使用的总内存量。其中包括但不限于以下内容:

  • JVM本身的基本内存开销
  • 堆空间(通过-Xmx进行限制)
  • 永久代空间(-XX:MaxPermSize,在所有JVM中都不是标准的)
  • 线程堆栈空间(每个堆栈通过-Xss进行设置),这可能会根据线程数量显著增长
  • 由本地分配所使用的空间(使用ByteBufer类或JNI)

不使用字节缓冲区(除非集合框架中有使用),限制PermGen似乎没有改变任何内容。我调用了System.arraycopy,我相信它使用JNI,是这个原因吗? - dspyz
@dspyz:我不认为arrayCopy会使用堆空间之外的额外空间。它将数据从一个堆位置复制到另一个堆位置。我建议您重新检查应用程序生成的线程数。 - Eyal Schneider
不,单线程的。在Eclipse调试器中我只看到主线程、信号分发器、终结器和引用处理程序。 - dspyz
@dspyz:进程的共享内存怎么样?我认为你可以使用jmap(jmap <process id>)来检查它。当您启动多个Java进程时,它们的总内存不是由top显示的总和,因为它们使用共享库。 - Eyal Schneider
它还包括共享库/动态链接库的大小,以及操作系统用于缓存已加载类文件的空间等。 - Ingo
此外,垃圾回收需要一些内存。https://dev59.com/rG445IYBdhLWcg3wcZ8N - Ivan Balashov

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

堆最大内存使用-Xmx,堆最小内存使用-Xms,栈内存使用-Xss,永久代最大尺寸使用-XX maxPermSize。

下面的示例说明了这种情况。我已经使用以下启动参数启动了我的Tomcat:

-Xmx168m -Xms168m -XX:PermSize=32m -XX:MaxPermSize=32m -Xss1m

6

使用-Xmx参数可以配置堆大小,使用-Xss参数可以配置栈大小。这两个参数的总和应该大致符合您的要求:

-Xmx150m -Xss50m

例如。
此外,还有一个参数-XX:MaxPermSize,它控制着。该参数对于-client的默认值为32MB,对于-server的默认值为64MB。根据您的配置也要计算它。PermGen空间是:

永久代用于保存VM本身的反射信息,如类对象和方法对象。

因此,它基本上存储了JVM的内部数据,例如类定义和interned字符串。
最后我必须说,有一部分是您无法控制的,那就是由本机Java进程使用的内存。Java是一个程序,就像其他任何程序一样,因此它也使用内存。如果您在任务管理器中观察内存使用情况,您将看到这个内存以及您的程序内存消耗。

不要忘记 PermGen 空间。 - danpaq
谢谢,我刚刚找到并尝试了一下。但是没有任何改变。我没有使用堆栈内存。如果不是堆或栈,它可能来自哪里? - dspyz
什么是PermGen空间,如何限制它? - dspyz
是的,但我之前运行过使用不到300m内存的Java程序。本地Java进程使用的内存只是固定的开销,不是吗? - dspyz
当我说“之前”,我的意思是几周前。我们的教授对我们所有的项目都施加了相同的200米限制。我以前从未见过这种情况发生过。 - dspyz
显示剩余4条评论

5
重要的是要注意,“总内存使用量”(在Linux中为RSS)包括JDK堆(+其他JDK区域)以及分配的任何“本机内存”。
例如,这些人发现在GC之间分配太多的jaxbcontexts(具有关联本机内存)可能会导致它使用大量额外的RAM。另一个常见的问题似乎是如果您不调用ZipInflater(或GZipStream等)上的close,则会出现此问题。

http://sleeplessinslc.blogspot.com/2014/08/jvm-native-memory-leak.html

他的最终解决方法是要么更频繁地进行垃圾回收(使用GC1垃圾收集器或指定较小的-Xmx设置),要么缓存JaxBContext对象(因为它们没有close方法,所以无法控制泄漏)。

还要注意,有时候可以通过检查jstack找到内存问题: http://javaeesupportpatterns.blogspot.com/2011/09/jaxbcontext-performance-problem-case.html

有时也可能会“漏掉”关闭例如GZipStreams的方式 http://kohsuke.org/2011/11/03/quiz-time-memory-leak-in-java


1

似乎没有适用于Linux的版本可用。 - dspyz
2
它应该在JDK的bin目录中(而不是JRE),这可能不在您的PATH路径中。 - danpaq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接