jvisualvm/jconsole中的system.gc()vs gc按钮



我目前正在测试我的概念验证验证原型涉及XML模式,并围绕着一个非常记忆的构建,该记忆消耗了Tree Automata的外部库(我有来源),我'd喜欢绘制"真实峰"(堆)不同运行的记忆消耗,而架构尺寸的增加(使用的度量标准适合我的目的,并且不影响问题),或者至少是合理的近似值。

>

给出一个数量级,对于具有100MB的真实峰的运行(我测试了它运行几次,与输入/参数的配置完全相同,迫使使用-XMX和-XMS的JVM内存降低值,I在线程" main" java.lang.outofmemoryerror中获取例外:GC高架限制超过< 100MB,并具有稳定且可重复的结果),它在1.1GB左右占据,这就是为什么我对我来说非常重要的实际数字,因为它们有很大不同!

我在过去的10天里一直在阅读网络和stackoverflow上的问题,我实际上知道的是:

  1. system.gc()"建议" gc运行,不以任何方式强制它,因此不可能依靠它来检测内存使用峰

  2. 通常建议的是计算对象职由于创建了很多收集(集,列表和映射)迭代器,以不同的方法称为非常高的次数(例如,我记得的时间为10分钟),因此这将非常困难要检测所有涉及的对象并执行总和(我在数天内通过内存消耗图进行了许多运行,而无需仅识别一个瓶颈)

  3. 无法轻松获得方法的内存占用(表示为对象存储器分配的峰值)

事实是,我自己经历了system.gc()调用不是可靠的(例如,由于GC的确被调用,同一配置的不同运行,在System.gc()之后读取不同的内存)。,但是当我按JVisualVM中的" GC按钮"或JConsole 从不运行GC或拒绝这样做。

所以我的问题是:调用他们对该按钮的实现(我还没有尝试过,但是对于我现在阅读的内容,使用jconsole.jar.jar with actact api)与调用会有所不同直接从我的代码中直接从我的代码中解决了我的问题?如果不是,您如何解释该按钮的" cestricinistc行为"?

到目前为止,我对10个增加的模式大小进行了一些实际存储峰的手动测试(对于这种测量,模式是从单个"复杂性参数"自动生成的),如果我不将绘制预期的曲线,我绘制了预期的曲线能够获得一个更好的解决方案,我想将代码作为外部罐子作为外部JAR,其以-xmx/-XMS等于我对预期内存峰的预测略小,从实现了完整的运行。(如果天真的内存预测不够强大,我将应用适当的机器学习技术)。我知道这不是一个优雅的解决方案,而是在我的情况下(学术界),我有能力花一些时间进行这些测量。如果您对这种蛮力方法有其他建议或改进,您会(非常)分享它们。

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

Java版本" 1.7.0_04" Java(TM)SE运行时环境(构建1.7.0_04-B20) Java热点(TM)64位服务器VM(构建23.0-B21,混合模式)

预先感谢Alessandro

据我所知,JConsole或任何其他工具仅使用System.gc()。没有其他选择。众所周知,Java告诉每个人不要依靠System.gc(),但这并不意味着它根本不起作用。

因此,您似乎很担心如何直接按下该按钮直接调用GC&仍然Java说System.gc仅"建议"致电GC。我说,该按钮还调用system.gc()&它只是"建议" Java尝试GC,&以某种方式,Java决定在当时执行GC本身(它不能保证,但Java以某种方式做到了。)

因此,为了证明这一事实,我刚刚创建了简单的程序,该程序只是创建了许多对象。它已经评论了" system.gc()"。现在,尝试使用注释System.gc()&然后通过uncomenting 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;
    }
}

在这里部分输出我的机器。

CONSENED SYSTEM.GC():此处gc在JRE的WILL中调用。

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

uncomented 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,只是我们从未看过。

我对这种微不足道的方法有很多积极的经验:

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

由于对象最终确定问题,通常在最终确定中可以复活对象的一个GC运行不足。因此,在第二个GC运行中发布了其他内存。

确实指出,这以及其他似乎"更聪明"的方法是启发式方法,并且完全取决于JVM的确切版本,包括其GC配置。但是在许多情况下,您对通用性不会太感兴趣:如果它现在有效并允许您进行测量,那么这是要走的方式。

'>

1)system.gc()"建议" gc运行,不以任何方式强迫它,因此不可能依靠它来检测内存使用峰

那就是规格所说的话,但是如果您使用openjdk或热点,除非将其关闭,否则它将始终执行完整的GC。

通常建议的是计算对象职业

我建议使用商业记忆探查器。我的JVM最多从8 GB开始,看看它尝试使用多少。之后,我会根据您对它是否想要或不使用它的判断来增加或减少它。

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

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

如果不是,您如何解释该按钮的"确定性行为"?

我会将其放在主观分析中。;)

理想情况下,您应该使用2-3x运行JVM,这是它有效运行所需的最小内存。试图节省数量低于$ 1的100 MB并不总是有用的。;)

您可以像这样有点gc。

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

除此之外...我有兴趣看看这个问题去了哪里。