JVM内存使用失控

时间:2012-08-01 22:41:47

标签: java jvm visualvm

我有一个Tomcat webapp,代表客户执行一些漂亮的内存和CPU密集型任务。这是正常的,是所需的功能。但是,当我运行Tomcat时,内存使用量会随着时间的推移而猛增至4.0GB以上,此时我通常会杀死该进程,因为它会破坏我在开发计算机上运行的所有其他内容:

enter image description here

我以为我无意中用我的代码引入了内存泄漏,但在用VisualVM检查后,我看到了另一个故事:

enter image description here

VisualVM显示堆占用大约一GB的RAM,这就是我用CATALINA_OPTS="-Xms256m -Xmx1024"设置的。

为什么我的系统看到这个过程占据了大量的内存,而根据VisualVM,它几乎没有占用任何内容?


经过一番进一步的嗅探,我注意到如果在应用程序中同时运行多个作业,则内存不会被释放。但是,如果我等待每个作业完成,然后再向BlockingQueue提供的ExecutorService提交另一个作业,那么内存将被有效回收。我该怎么调试呢?为什么垃圾收集/内存重用会有所不同?

2 个答案:

答案 0 :(得分:9)

您无法控制要控制的内容-Xmx仅控制Java堆,它不控制本机内存的消耗JVM,根据实现完全不同地使用。 VisualVM只显示Heap正在消耗的内容,它不会显示整个JVM作为操作系统进程的本机内存所消耗的内容。您将不得不使用操作系统级工具来查看,并且它们将报告完全不同的数字,通常比VisualVM报告的任何数字都大得多,因为JVM以完全不同的方式使用本机内存。 / p>

来自以下文章Thanks for the Memory ( Understanding How the JVM uses Native Memory on Windows and Linux )

维护堆和垃圾收集器使用您无法控制的本机内存。

  

需要更多的本机内存来维护状态   内存管理系统维护Java堆。数据结构   必须分配以跟踪免费存储并记录进度   收集垃圾。这些数据结构的确切大小和性质   随实施而变化,但许多与大小成正比   堆。

并且JIT编译器使用本机内存,就像javac

一样
  

字节码编译使用本机内存(与静态内存相同)   编译器如gcc需要内存运行),但两者都是输入(   字节码)和JIT的输出(可执行代码)也必须   存储在本机内存中。包含许多的Java应用程序   JIT编译的方法比较小的应用程序使用更多的本机内存。

然后你有使用本机内存的类加载器

  

Java应用程序由定义对象结构的类组成   和方法逻辑。它们还使用Java运行时类中的类   库(如java.lang.String)并可能使用第三方   库。只要这些类需要存储在内存中   他们正在被使用。如何存储类因实现而异。

我甚至不会开始引用关于Threads的部分,我想你明白了 -Xmx不控制您认为它控制的内容,它控制JVM堆,而不是所有内容 进入JVM堆,并且堆占用了您指定的更多本机内存 管理和簿记。

Plain and simple the JVM uses more memory than what is supplied in -Xms and -Xmx and the other command line parameters.

这是一个very detailed article on how the JVM allocates and manages memory,根据您在问题中的假设,它并不像您预期​​的那样简单,值得全面阅读。

许多实现中的ThreadStack大小具有最小限制,这些限制因操作系统而有时因JVM版本而异;如果将限制设置为低于JVM或OS的本机操作系统限制,则会忽略threadstack设置(有时必须设置ulix on * nix)。其他命令行选项以相同的方式工作,当提供的值太小时,默认默认为更高的值。不要假设传入的所有值都代表实际使用的值。

类加载器和Tomcat有不止一个,占用了大量内存,而这些内存很难被记录下来。 JIT占用了大量的内存,随着时间的推移交易空间,这在大多数情况下是一个很好的交易。

答案 1 :(得分:0)

您还应检查CPU使用情况和垃圾收集器 garbage collection pauses和CPU gc可能会进一步降低您的机器速度。