我正在创建和插入相当轻的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
为什么堆大小实际上增加了?
答案 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之后的内存消耗,其他任何事情都是最不费力的。