JVisualVM / JConsole中的System.gc()vs GC按钮

我目前正在测试我的概念certificate原型处理XML模式,并围绕一个非常耗费内存的外部库构建树自动机(我已经获得了源代码),我想绘制“真正的峰值”(堆) )随着模式大小的增加,不同运行的内存消耗(使用的度量标准适合我的purpouse并且不影响问题),或者至少是它的合理近似值。

为了给出一个数量级,对于一个真正的峰值为100MB的运行(我测试它运行几次完全相同的输入/参数配置,强制jvm内存-Xmx和-Xms减少值,我得到Exception in线程“main”java.lang.OutOfMemoryError:GC开销限制超过 <100MB,具有稳定且可重复的结果)它占用大约1.1GB,这就是为什么获取实数非常重要,因为它们差别很大!

我花了最近10天在网上和stackoverflow中阅读问题,我实际知道的是:

  1. System.gc()“建议”GC运行,不以任何方式强制它,因此不可能依赖它来检测内存使用峰值

  2. 通常建议的是计算对象占用(我看到了SizeOf项目,我尝试并且工作正常,即使它不符合我的需要),这对我来说是不可行的,因为由于创建了大量的内存分配很多收集(设置,列表和映射)迭代器在不同的方法中,被称为非常多次(对于我记忆中的10分钟,每个数百万次),因此检测所有涉及的对象将非常困难并执行总和(我用内存消耗图调试了很多次运行而不能只识别一个瓶颈)

  3. 没有办法轻易获得方法的内存占用(表示为对象内存分配的峰值)

事实上,我自己经历过System.gc()调用不可靠(例如,由于GC真的被调用而在System.gc()之后读取不同的内存),但是当我按下JVisualVM或Jconsole中的“GC按钮”它永远不会运行GC或拒绝这样做。

所以我的问题是:调用它们的按钮实现(我还没有尝试过,但是对于我现在所读到的,使用带有attach api的 jconsole.jar似乎是可行的)将不同于直接调用System.gc()从我的代码,从而解决我的问题? 如果没有,你如何解释该按钮的“确定性行为”?

到目前为止,我对10个增加的模式大小进行了实际内存峰值的手动测试(对于这种测量,模式是从单个“复杂度参数”自动生成的)并且我绘制了预期曲线,如果我将无法获得一个更好的解决方案我想将我的代码作为外部jar运行,其中-Xmx / -Xms等于略低于我对预期内存峰值的预测,捕获外部进程ErrorStream中的OutMemoryException并重新启动增加的内存直到完成运行已完成。 (如果天真的记忆预测不够健壮,我将应用适当的机器学习技术)。 我知道这不是一个优雅的解决方案,但在我的情况下(学术界)我可以花费一些额外的时间来进行这些测量。 如果您对此powershell方法有其他建议或改进,欢迎(非常)欢迎您分享。

系统信息(机器是Fedora 17,64位):

java版“1.7.0_04”Java(TM)SE运行时环境(版本1.7.0_04-b20)Java HotSpot(TM)64位服务器VM(版本23.0-b21,混合模式)

在此先感谢,亚历山德罗

我对这个简单的方法有很多积极的经验:

 System.gc(); Thread.sleep(500); System.gc(); 

由于对象终结问题,一个GC运行通常是不够的,其中对象可能在最终确定中复活。 因此,在第二次GC运行中会释放额外的内存。

请注意,这个以及其他看似“更聪明”的方法都是启发式方法,并且完全依赖于JVM的确切版本,包括其GC配置。 但在许多情况下,你对通用性不会那么感兴趣:如果它现在正常工作并允许你进行测量,那么这就是你要走的路。

据我所知,Jconsole或任何其他工具仅使用System.gc()。 没有其他选择。 众所周知,java告诉大家不要依赖System.gc(),但这并不意味着它根本不起作用。

所以来到你的查询,你似乎担心如何按下该按钮直接调用GC仍然java说System.gc只“建议”调用GC。 我说,该按钮也调用System.gc()并且它只是“建议”java尝试GC,并且它发生在某种程度上java决定在那个时候执行GC(它不能保证但不知何故java会这样做。)

所以为了certificate这一点,我只创建了一个简单的程序,它只是创建了大量的对象。 它注释了“System.gc()”。 现在尝试首先使用注释的System.gc()然后通过取消注释System.gc()来运行相同的程序。 确保提供VM参数为-verbose:gc -XX:+ PrintGCTimeStamps -XX:+ PrintGCDetails。

 package ravi.tutorial.java.gc; /** * Just to test GC. RUn with below VM arguments. * * -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails * * * @author ravi.k * */ public class TestGC { public static A a; /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 100; i++) { populateObjects(); System.out.println("population done for batch: " + i); } } public static void populateObjects() { for (int i = 0; i < 100000; i++) { a = new A("A"); } //System.gc(); } } class A { String s; public A(String s) { this.s = s; } } 

这里部分输出我的机器。

Commened System.gc():此处随机调用GC。

 population done for batch: 0 population done for batch: 1 population done for batch: 2 population done for batch: 3 population done for batch: 4 population done for batch: 5 population done for batch: 6 population done for batch: 7 population done for batch: 8 population done for batch: 9 0.332: [GC 0.332: [ParNew: 17024K->410K(19136K), 0.0024479 secs] 17024K->410K(83008K), 0.0025219 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] population done for batch: 10 population done for batch: 11 population done for batch: 12 population done for batch: 13 population done for batch: 14 population done for batch: 15 population done for batch: 16 population done for batch: 17 population done for batch: 18 population done for batch: 19 0.344: [GC 0.344: [ParNew: 17434K->592K(19136K), 0.0011238 secs] 17434K->592K(83008K), 0.0011645 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] population done for batch: 20 population done for batch: 21 population done for batch: 22 population done for batch: 23 population done for batch: 24 population done for batch: 25 population done for batch: 26 population done for batch: 27 population done for batch: 28 population done for batch: 29 population done for batch: 30 0.353: [GC 0.353: [ParNew: 17616K->543K(19136K), 0.0011398 secs] 17616K->543K(83008K), 0.0011770 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] population done for batch: 31 population done for batch: 32 population done for batch: 33 

取消注释System.gc():此处为每个批次调用GC。 现在System.gc()只是建议GC,但是java选择在那个时候运行GC。 这与其他工具中的魔术GC按钮完全相同:)

 0.337: [Full GC (System) 0.337: [CMS: 0K->400K(63872K), 0.0219250 secs] 3296K->400K(83008K), [CMS Perm : 4423K->4422K(21248K)], 0.0220152 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] population done for batch: 0 0.364: [Full GC (System) 0.364: [CMS: 400K->394K(63872K), 0.0161792 secs] 2492K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0162336 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] population done for batch: 1 0.382: [Full GC (System) 0.382: [CMS: 394K->394K(63872K), 0.0160193 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0160834 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] population done for batch: 2 0.399: [Full GC (System) 0.399: [CMS: 394K->394K(63872K), 0.0160866 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0161489 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] population done for batch: 3 0.417: [Full GC (System) 0.417: [CMS: 394K->394K(63872K), 0.0156326 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0156924 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] population done for batch: 4 0.434: [Full GC (System) 0.434: [CMS: 394K->394K(63872K), 0.0157274 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0157897 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] population done for batch: 5 

要添加更多,它就像线程一样。 线程运行时无法保证,但每当我们编写任何示例线程程序时,线程就会自行运行该时间。 所以我们不应该责怪java如何在线程启动时运行:)。 Java只是说不依赖这些东西,但它们确实有效。 虽然它们在某些情况下起作用并不意味着它们每次都会起作用。 即使是那些jconsole工具也可能无法执行GC,只是我们从未见过它。

1)System.gc()“建议”GC运行,不以任何方式强制它,因此不可能依赖它来检测内存使用峰值

这就是规范所说的,但是如果你使用OpenJDK或HotSpot它将始终执行Full GC,除非你关闭它。

通常建议的是计算对象占用

我建议使用商业内存分析器。 我会让JVM以最大8 GB的速度启动并查看它尝试使用多少。 在此之后,我会根据您对是否愿意使用它的判断来增加或减少它。

没有办法轻易获得方法的内存占用(表示为对象内存分配的峰值)

方法使用的唯一内存是堆栈。 您可以跟踪在方法中创建的对象(计数,类,大小)的数量,但这些对象不属于该方法,并且可以在任何地方使用,即使在返回方法之后也是如此。

如果没有,你如何解释该按钮的“确定性行为”?

我会把它归结为主观分析。 ;)

理想情况下,您应该以运行高效运行所需的最小内存的2-3倍运行JVM。 试图节省几百美元,其成本低于1美元并不总是有用。 ;)

你可以像这样强迫GC ….

 private static void force_gc() { Object obj = new Object(); WeakReference ref = new WeakReference(obj); obj = null; while (ref.get() != null) { Log.d(LOGTAG, "Forcing gc() ..."); System.gc(); } } 

除此之外……我有兴趣看看这个问题在哪里。