3

当我运行一个起始堆大小为 3G(由 -Xms3072m VM 参数设置)的 java 程序时,JVM 不会以该大小开始。它从 400m 左右开始,然后根据需要不断获取更多内存。

这对我来说是一个严重的问题。我知道 JVM 会在一段时间后需要上述数量。当JVM根据需要增加它的内存时,它就会变慢。在 JVM 获取更多内存期间,大量时间用于垃圾收集。而且我认为内存获取是一项昂贵的任务。

如何确保 JVM 实际上尊重起始堆大小参数?

更新:这个应用程序创建了很多对象,其中大部分很快就死掉了。一些生成的对象需要保留在内存中(从年轻堆中转移出来)。在此操作期间,所有这些对象都需要在内存中。运行后,可以看到young heap中的所有对象都被认领成功了。所以没有内存泄漏。

当堆大小达到 3G 时,同样的操作可以顺利运行。这清楚地表明需要额外的时间用于获取内存。

这个 Sun JDK 5。

4

4 回答 4

3

如果我没记错的话,Java 会尝试从操作系统获取内存预留。因此,如果您要求 3 GB 作为 Xms,Java 会询问操作系统,如果这可用但不是立即从所有内存开始......它甚至可能保留它(而不是分配它)。但这些都是细节。

通常,JVM 在开始严重的老年代垃圾收集之前会运行到 Xms 大小。年轻代 GC 一直在运行。通常 GC 仅在老一代 GC 运行且 VM 介于 Xms 和 Xmx 之间时才明显,或者,如果您将其设置为相同的值,则大致达到 Xmx。

如果您需要大量内存来存储短期对象,请通过将年轻区域设置为...来增加该内存区域...假设 1 GB -XX:NewSize=1g因为从年轻“桶”中移动“垃圾”成本很高“进入老一代。因为万一它还没有变成真正的垃圾,JVM 会检查垃圾,找不到任何垃圾,在幸存者空间之间复制它,最后移动到老一代。因此,当您知道自己没有任何垃圾并以某种方式推迟时,请尝试抑制对年轻一代的垃圾检查...

试试看!

于 2009-05-26T21:56:02.577 回答
2

我相信您的问题不是来自您的想法。

看起来最让您付出代价的是 GC 周期,而不是堆大小的分配。如果您确实在创建和删除大量对象。

你应该把精力集中在分析上,找出究竟是什么让你付出了这么多,并努力重构它。

我的预感——对象创建和删除,以及 GC 循环。

在任何情况下,-Xms都应该设置最小堆大小(如果不是 Sun ,请与您的 JVM 一起检查)。仔细检查以了解您认为不是这种情况的确切原因。

于 2009-05-14T08:39:22.927 回答
0

我使用了 sun 的 vm 并从最低设置为 14 gigs 开始,它确实从那开始。也许你应该尝试将 xms 和 xmx 值设置为相同的 amt,即试试这个 - -Xms3072m -Xmx3072m

于 2009-05-14T09:06:12.580 回答
0

为什么你认为堆分配不正确?使用任何仅显示 400m 的操作系统工具并不意味着它没有被分配。

我真的不明白你在追求什么。400m 及以上是否已经成为问题,或者您的程序应该需要那么多?如果您确实需要处理这么多内存,并且您似乎需要很多对象,那么您可以做几件事:

如果内存消耗与您的直觉不匹配,那么它可能是正确的数量,而不是您可能正在泄漏内存。这可以解释为什么它会随着时间的推移而“减速”。也许您错过了从一个结构中删除对象,这样它们就不会被垃圾收集,并且会减慢查找速度等。

您的内存设置本身可能就是问题所在。垃圾收集本身不运行。只有在达到某个阈值时才会调用它。如果你给它一个大的堆设置并且你的操作系统有足够的内存,那么垃圾收集不会经常运行。

您提到的特征将是创建大量对象并在它们再次被删除后不久的情况。否则垃圾收集不会成为问题(某种分代 gc)。这意味着你只有“年轻”的对象。如果您仅在短时间内需要对象,请考虑使用对象池。那将完全消除垃圾收集。

如果您知道代码中有运行 gc 的好时机,您可以考虑手动运行它以查看它是否会改变任何内容。这就是你需要的

  Runtime r = Runtime.getRuntime();
  r.gc();

这仅用于调试目的。gc 在大多数情况下都做得很好,因此不需要您自己调用 gc。

于 2009-05-14T09:07:52.903 回答