1

我正在创建和插入相当轻的对象,这些对象在 Drool 的工作记忆Person中具有一个字段。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

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

4

2 回答 2

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);
    }
}

我发现这一点很困难……我的撤回代码与您最初的假设相同。:)

于 2013-11-11T09:27:04.180 回答
1

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

收集内存是昂贵的,所以它只在必要时才这样做,而不是在可能的时候。

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

删除对象可以做一些工作,最终会创建临时对象。

基本上,您应该只在 Full GC 之后查看内存消耗,其他任何事情都是以最少的努力为基础的。

于 2013-11-11T03:13:11.770 回答