从Drool的工作记忆中删除事实后,堆大小没有减少

时间:2013-11-11 03:09:50

标签: java memory-management memory-leaks heap drools

我正在创建和插入相当轻的Person个对象,这些对象在Drool的工作记忆中有一个字段age。但即使删除了事实,堆大小也没有减少。示例代码 - (使用来自maven的Drools 6.0.0.CR5)

    long numOfFacts=1000000;

    long heapSize = Runtime.getRuntime().totalMemory();
    System.out.println("Heapsize before insertion: "+heapSize);

    System.out.println("Inserting objects");
    ArrayList<FactHandle> factHandles = new ArrayList<FactHandle>(100);
    for (int i = 0; i < numOfFacts; i++) {
        Person person = new Person();
        person.setAge(randomGenerator.nextInt(100));
        FactHandle factHandle = wkmem.insert(person);
        factHandles.add(factHandle);

    }

    long heapSizeAfter = Runtime.getRuntime().totalMemory();
    System.out.println("Heapsize after insertion: "+heapSizeAfter);


    long endTimeInsert = System.currentTimeMillis();
    long elTime= endTimeInsert-startTimeInsert;
    System.out.println("Time it took to insert " +numOfFacts+" objects :"+elTime+" milliseconds");

    long startTime = System.currentTimeMillis();
    System.out.println("Number of facts: " + wkmem.getFactCount());

    wkmem.fireAllRules();
    long stopTime = System.currentTimeMillis();
    long elapsedTime = stopTime - startTime;
    System.out.println("Time it took for evaluation: " + elapsedTime);
    for(int i=0;i<numOfFacts;i++){
        wkmem.retract(factHandles.get(i));
    }

    long heapSizeAfterRemoval = Runtime.getRuntime().totalMemory();
    System.out.println("Heapsize after removal of facts: "+heapSizeAfterRemoval);

代码的输出是 -

Heapsize before insertion: 158138368
Inserting objects
Heapsize after insertion: 746717184
Time it took to insert 1000000 objects :5372 milliseconds
Number of facts: 1000000
Time it took for evaluation: 839
Heapsize after removal of facts: 792002560

为什么堆大小实际上增加了?

2 个答案:

答案 0 :(得分:3)

如Peter Lawrey的回答所述,你不会在方法中间看到堆大小减少。除非GC恰好恰好在那个时刻开始。要测试它,你需要有一个长期运行的应用程序,并使用JConsole之类的东西连接它或使用某种类型的分析器。

但是,值得注意的是,缩回的方式不可靠,并且在某些情况下会导致内存泄漏。事实上,在某些情况下,Drools会在内部生成FactHandles,因此在收回与您自己的事实处理引用相关的所有事实后,可能会有更多的工作记忆。如果我没记错的话,它们会保留对您的事实的引用,从而防止这些对象被垃圾收集。因此,只需收回所有事实句柄就会更安全:

public void retractAll() {
    for (FactHandle handle : ksession.getFactHandles()) {
        retract(handle);
    }
}

...或撤消所有FactHandles以获取过滤器:

public void retractAll(ObjectFilter filter) {
    for (FactHandle handle : ksession.getFactHandles(filter)) {
        retract(handle);
    }
}

我发现这很难......我的撤销代码与你原来的假设相同。 :)

答案 1 :(得分:1)

堆大小始终保持不变或增加,直到GC需要运行或决定运行(对于并发收集器)

收集内存非常昂贵,因此只有在必要时才会这样做,而不是在它可能的时候。

  

为什么堆大小实际上已经增加?

删除对象可以完成一些最终可能会创建临时对象的工作。

基本上你应该只考虑Full GC之后的内存消耗,其他任何事情都是最不费力的。