我有一个Tomcat webapp,代表客户执行一些漂亮的内存和CPU密集型任务。这是正常的,是所需的功能。但是,当我运行Tomcat时,内存使用量会随着时间的推移而猛增至4.0GB以上,此时我通常会杀死该进程,因为它会破坏我在开发计算机上运行的所有其他内容:
我以为我无意中用我的代码引入了内存泄漏,但在用VisualVM检查后,我看到了另一个故事:
VisualVM显示堆占用大约一GB的RAM,这就是我用CATALINA_OPTS="-Xms256m -Xmx1024"
设置的。
为什么我的系统看到这个过程占据了大量的内存,而根据VisualVM,它几乎没有占用任何内容?
经过一番进一步的嗅探,我注意到如果在应用程序中同时运行多个作业,则内存不会被释放。但是,如果我等待每个作业完成,然后再向BlockingQueue
提供的ExecutorService
提交另一个作业,那么内存将被有效回收。我该怎么调试呢?为什么垃圾收集/内存重用会有所不同?
答案 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堆,并且堆占用了您指定的更多本机内存
管理和簿记。
这是一个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可能会进一步降低您的机器速度。