[jboss-jira] [JBoss JIRA] (DROOLS-498) Memory leak in stateful session after retracting facts

Kent Anderson (JIRA) issues at jboss.org
Tue Jul 15 16:06:30 EDT 2014


    [ https://issues.jboss.org/browse/DROOLS-498?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12985354#comment-12985354 ] 

Kent Anderson edited comment on DROOLS-498 at 7/15/14 4:04 PM:
---------------------------------------------------------------

Thanks for checking back on this, Davide.

My workstation is running Oracle Java 1.7.0_60 on MacOS.  Our test environment is Linux: CentOS 6 with Oracle Java 1.7.0_25.  The problem shows up in both environments.

Here is the listing of the test case...

{code}
 @Test
    public void testDurationMemoryLeak() throws Exception {
        String drl = 
                "import org.drools.compiler.StockTick;\n " +
                "declare StockTick\n"+
                "   @role( event )\n"+
                "   @timestamp( time )\n"+
                "end\n"+
                
                "rule \"announce DROO\"\n"+
                "   no-loop\n"+
                "when\n"+
                "   $droo : StockTick( company == \"DROO\" )\n"+
                "then\n"+
                "   System.out.println(\"Received the DROO\");\n"+
                "end\n"+

                "rule \"delay DROO\"\n"+
                "    no-loop\n"+
                "    duration(2s)\n"+
                "when\n"+
                "    $droo :  StockTick( company == \"DROO\" )\n"+
                "then\n"+
                "    System.out.println(\"forwarding the DROO\");\n"+
                "    delete($droo);\n"+
                "end\n"+

                "rule \"cancel RAISE\"\n"+
                "    no-loop\n"+
                "when\n"+
                "    $oord : StockTick( company == \"OORD\" )\n"+
                "    $droo : StockTick( company == \"DROO\" )\n"+
                "then\n"+
                "    System.out.println(\"Canceling the DROO due to a OORD\");\n"+
                "    delete($droo);\n"+
                "    delete($oord);\n"+
                "end";


        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newByteArrayResource(drl.getBytes()), ResourceType.DRL);
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KieBaseConfiguration baseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
        baseConfig.setOption( EventProcessingOption.STREAM );
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase( baseConfig );
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        KieSession ksession = kbase.newKieSession();
        StatefulKnowledgeSessionImpl internalSession = (StatefulKnowledgeSessionImpl) ksession;

        assertEquals("FactCount should be 0[1]", 0, ksession.getFactCount());

        FactHandle hRaise = ksession.insert(new StockTick(0, "DROO", 1.00));
        ksession.fireAllRules();
        assertEquals("FactCount should be 1[2]", 1, ksession.getFactCount());

        Thread.sleep(500);

        ksession.insert(new StockTick(1, "OORD", 1.00));
        ksession.fireAllRules();
        assertEquals("FactCount should be 0[3]", 0, ksession.getFactCount());

        Thread.sleep(500);

        hRaise = ksession.insert(new StockTick(2, "DROO", 1.00));
        ksession.fireAllRules();
        assertEquals("FactCount should be 1[4]", 1, ksession.getFactCount());

        Thread.sleep(2100);
        
        ksession.fireAllRules();
        assertEquals("FactCount should be 0[5]", 0, ksession.getFactCount());

        assertTrue("Object should no longer exist", 
                null == internalSession.getObjectStore().getObjectForHandle(hRaise));

        ((DefaultAgenda)ksession.getAgenda()).getGarbageCollector().forceGcUnlinkedRules();	

        /*
         * Uncomment the following while loop to hold the JVM open so you can 
         * grab a heap dump using JVisualVM.  Execute the following OQL query
         * to see that the StockTick(2, "DROO", 1.00) object that was the final
         * event inserted above is still referenced in memory.  It should not
         * be.
         */

        while(true){
            Thread.sleep(10000);
        }

        // Final assert needed: need to verify that the StockTock(2, "DROO", 1.00)
        // object is no longer referenced in any of the internal Drools data 
        // structures.  Not sure how to do this using the Drools api.
        
    }
{code}


was (Author: kanderson.psware):
Thanks for checking back on this, Davide.

Here is the listing of the test case...

{code}
 @Test
    public void testDurationMemoryLeak() throws Exception {
        String drl = 
                "import org.drools.compiler.StockTick;\n " +
                "declare StockTick\n"+
                "   @role( event )\n"+
                "   @timestamp( time )\n"+
                "end\n"+
                
                "rule \"announce DROO\"\n"+
                "   no-loop\n"+
                "when\n"+
                "   $droo : StockTick( company == \"DROO\" )\n"+
                "then\n"+
                "   System.out.println(\"Received the DROO\");\n"+
                "end\n"+

                "rule \"delay DROO\"\n"+
                "    no-loop\n"+
                "    duration(2s)\n"+
                "when\n"+
                "    $droo :  StockTick( company == \"DROO\" )\n"+
                "then\n"+
                "    System.out.println(\"forwarding the DROO\");\n"+
                "    delete($droo);\n"+
                "end\n"+

                "rule \"cancel RAISE\"\n"+
                "    no-loop\n"+
                "when\n"+
                "    $oord : StockTick( company == \"OORD\" )\n"+
                "    $droo : StockTick( company == \"DROO\" )\n"+
                "then\n"+
                "    System.out.println(\"Canceling the DROO due to a OORD\");\n"+
                "    delete($droo);\n"+
                "    delete($oord);\n"+
                "end";


        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newByteArrayResource(drl.getBytes()), ResourceType.DRL);
        if ( kbuilder.hasErrors() ) {
            fail( kbuilder.getErrors().toString() );
        }

        KieBaseConfiguration baseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
        baseConfig.setOption( EventProcessingOption.STREAM );
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase( baseConfig );
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );

        KieSession ksession = kbase.newKieSession();
        StatefulKnowledgeSessionImpl internalSession = (StatefulKnowledgeSessionImpl) ksession;

        assertEquals("FactCount should be 0[1]", 0, ksession.getFactCount());

        FactHandle hRaise = ksession.insert(new StockTick(0, "DROO", 1.00));
        ksession.fireAllRules();
        assertEquals("FactCount should be 1[2]", 1, ksession.getFactCount());

        Thread.sleep(500);

        ksession.insert(new StockTick(1, "OORD", 1.00));
        ksession.fireAllRules();
        assertEquals("FactCount should be 0[3]", 0, ksession.getFactCount());

        Thread.sleep(500);

        hRaise = ksession.insert(new StockTick(2, "DROO", 1.00));
        ksession.fireAllRules();
        assertEquals("FactCount should be 1[4]", 1, ksession.getFactCount());

        Thread.sleep(2100);
        
        ksession.fireAllRules();
        assertEquals("FactCount should be 0[5]", 0, ksession.getFactCount());

        assertTrue("Object should no longer exist", 
                null == internalSession.getObjectStore().getObjectForHandle(hRaise));

        ((DefaultAgenda)ksession.getAgenda()).getGarbageCollector().forceGcUnlinkedRules();	

        /*
         * Uncomment the following while loop to hold the JVM open so you can 
         * grab a heap dump using JVisualVM.  Execute the following OQL query
         * to see that the StockTick(2, "DROO", 1.00) object that was the final
         * event inserted above is still referenced in memory.  It should not
         * be.
         */

        while(true){
            Thread.sleep(10000);
        }

        // Final assert needed: need to verify that the StockTock(2, "DROO", 1.00)
        // object is no longer referenced in any of the internal Drools data 
        // structures.  Not sure how to do this using the Drools api.
        
    }
{code}

> Memory leak in stateful session after retracting facts
> ------------------------------------------------------
>
>                 Key: DROOLS-498
>                 URL: https://issues.jboss.org/browse/DROOLS-498
>             Project: Drools
>          Issue Type: Bug
>      Security Level: Public(Everyone can see) 
>    Affects Versions: 6.0.1.Final
>            Reporter: Vitaly Lomov
>            Assignee: Mario Fusco
>            Priority: Critical
>         Attachments: gcRoot.png, memory-leak2.zip, screenshot1.png
>
>
> After adding many simple facts into the stateful session and then removing them, the factHandles for the removed facts are still referenced by the session. Eventually getting "java.lang.OutOfMemoryError: GC overhead limit exceeded" with the stack trace (sometimes) similar to DROOLS-411.
> Someone on StackOverflow suggested iterating through whatever factHandles remain after deletion and delete those, but I get no factHandles (correct, since I removed all facts).
> See screenshot for objects in memory _after_ deleting all facts.



--
This message was sent by Atlassian JIRA
(v6.2.6#6264)


More information about the jboss-jira mailing list