我最近将我的大型 Java 应用程序更改为以 JAR 而非单个类文件的形式交付。我有 405 个 JARS,其中包含 5000 个类文件。我的问题是,当我将程序作为 JAR 运行时(类路径是获取所有 JAR 的通配符),Java 将不断使用越来越多的内存。我已经看到内存超过 2GB,而且 Java 似乎没有执行停止世界的垃圾收集来降低内存。如果我针对展开的 JAR(仅类文件)运行完全相同的程序,Java 的内存使用率会低得多(< 256MB)并保持在那里。这发生在 Windows 7 (x64) 和 Windows Server (x64) 上的 Oracle Java 8 中。为什么将我的应用程序打包为 JAR 会更改内存配置文件?此外,我已经将程序作为 JAR 运行了很长时间,最大内存限制为 128MB,没有任何问题,所以我没有内存泄漏。
类路径中的 JAR 文件
类路径中的类文件
编辑:我接受了@K Erlandsson 的回答,因为我认为这是最好的解释,而这只是 Java 的一个丑陋的怪癖。感谢大家(尤其是@K Erlandsson)的帮助。
最佳答案
首先要注意的是,堆上完全使用了多少内存在任何时候都不是很有趣,因为使用的大部分内存可能是垃圾,将被下一次 GC 清除。
您需要关注的是 Activity 对象使用了多少堆。您在评论中写道:
I don't know if this matters, but if I use jvisualvm.exe to force a GC (mark sweep) the heap memory usage will drop clearing almost all the heap memory.
这很重要。 很多。这意味着当您在使用 jar 时看到更高的堆使用率时,您会看到更多的垃圾,而不是 Activity 对象消耗的更多内存。当您执行 GC 时垃圾被清除,一切都很好。
从 jar 文件加载类比从类文件加载它们暂时会消耗更多的内存。需要打开、查找和读取 jar 文件。与简单地打开特定的 .class
文件并读取它相比,这需要更多的操作和更多的临时数据。
由于大部分堆使用都被 GC 清除,因此您不需要非常关注这种额外的内存消耗。
你还写:
Java will continually use more and more memory. I have seen the memory go > 2GB and it seems like Java is not doing stop-the-world garbage collections to keep the memory lower.
这是典型的行为。 GC 仅在 JVM 认为有必要时运行。 JVM 将根据内存行为对此进行调整。
编辑:现在我们看到了您的 jConsole 图像,我们看到了提交堆内存的不同(250 MB 与 680 MB)。提交堆是堆的实际大小。这将有所不同(取决于您使用 -Xmx
设置的值),具体取决于 JVM 认为将为您的应用程序产生最佳性能的内容。但是,它主要会增加,几乎不会减少。
对于 jar 情况,JVM 已为您的应用程序分配了更大的堆。可能是由于在初始类加载期间需要更多内存。 JVM 然后认为更大的堆会更快。
当您拥有更大的堆、更多的提交内存时,在运行 GC 之前有更多的内存可供使用。这就是为什么您会看到两种情况下内存使用情况的差异。
底线:您看到的所有额外使用都是垃圾,而不是 Activity 对象,为什么您不需要担心这种行为,除非您有实际问题,因为内存将在下一次 GC。
关于Java JAR 内存使用 VS 类文件内存使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31079320/