Added memory leak test and fixed the leak by removing references 76/676/1
authorHannu Niemistö <hannu.niemisto@semantum.fi>
Tue, 4 Jul 2017 09:44:40 +0000 (12:44 +0300)
committerHannu Niemistö <hannu.niemisto@semantum.fi>
Tue, 4 Jul 2017 09:44:40 +0000 (12:44 +0300)
refs #7352

Change-Id: Idc412759d49314f3226e6b1648881aaab8248fe9

16 files changed:
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/SCLConstant.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSABlock.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAClosure.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAExit.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAFunction.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAModule.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAObject.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAStatement.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/If.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Jump.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Throw.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java
bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java
tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/MemoryLeakTest.java [new file with mode: 0644]

index 14bcbcaf65e84e645e99948b02c80a508033ef83..9089ebb817209e1735ebcaf3dcfd7e39d65585b9 100644 (file)
@@ -373,4 +373,11 @@ public class SCLConstant extends DelegateConstant implements Named {
         if(inlineArity < Integer.MAX_VALUE)
             inlinableDefinition = (SSAFunction)definition.copy();
     }
+
+    public void cleanup() {
+        if(definition != null)
+            definition.cleanup();
+        if(inlinableDefinition != null)
+            inlinableDefinition.cleanup();
+    }
 }
index ba9d62d27ce730bffc2b99772321406736125e07..60e156a7f0a683dbac408cf2d9b8ba8d61bfa035 100644 (file)
@@ -598,5 +598,12 @@ public final class SSABlock extends Cont implements Printable, BoundVarBinder {
             statement.forValRefs(visitor);
         exit.forValRefs(visitor);
     }
+
+    public void cleanup() {
+        for(SSAStatement statement = firstStatement;
+                statement != null; statement = statement.next)
+            statement.cleanup();
+        exit.cleanup();
+    }
     
 }
index 57748b284b436c727109433613296de9e6910148..3dd77d926b519c495436cb22324b9826386d513e 100644 (file)
@@ -104,5 +104,7 @@ public abstract class SSAClosure implements Printable, BoundVarBinder {
     }
 
     public abstract void forValRefs(ValRefVisitor visitor);
+
+    public abstract void cleanup();
     
 }
index c183699f9cb5512eb95fa943bfa41d73a1c8b318..ae9ad5725a436256b382fbd12853544286c06312 100644 (file)
@@ -71,4 +71,6 @@ public abstract class SSAExit implements Printable {
     }
 
     public abstract void forValRefs(ValRefVisitor visitor);
+
+    public abstract void cleanup();
 }
\ No newline at end of file
index b9e8bfbb398e0803977e305533259eb0eb6d4982..209c92a64af4016687339f559e6639369093a7bb 100644 (file)
@@ -154,7 +154,7 @@ public final class SSAFunction extends SSAClosure {
         
         // Add valid variables and continuations
         context.validContinuations.add(returnCont);        
-        for(SSABlock block = firstBlock; block != null; block = block.next) {            
+        for(SSABlock block = firstBlock; block != null; block = block.next) {
             context.validContinuations.add(block);  
             for(BoundVar parameter : block.parameters)
                 context.validBoundVariables.add(parameter);
@@ -461,4 +461,9 @@ public final class SSAFunction extends SSAClosure {
             block.forValRefs(visitor);
     }
 
+    @Override
+    public void cleanup() {
+        for(SSABlock block = firstBlock; block != null; block = block.next)
+            block.cleanup();
+    }
 }
index e0dbf214f3a8a9a32c2adcec8c2feb98ab7ae225..c9c8b35fb19c16a85714c027772d102c50e92c88 100644 (file)
@@ -219,4 +219,11 @@ public class SSAModule {
         for(SCLConstant function : functions.values())
             function.saveInlinableDefinition();
     }
+
+    public void cleanup() {
+        for(SSAClosure closure : closuresToGenerate)
+            closure.cleanup();
+        for(SCLConstant constant : functions.values())
+            constant.cleanup();
+    }
 }
index 56877e304c96097885db1587d904bc48ff45ed82..a5ce6f95b9447ea02bebd418318b0463a739690d 100644 (file)
@@ -144,4 +144,10 @@ public class SSAObject extends SSAClosure implements ClosureBinder {
             closure.forValRefs(visitor);
     }
 
+    @Override
+    public void cleanup() {
+        for(SSAClosure closure = firstClosure; closure != null; closure = closure.getNext())
+            closure.cleanup();
+    }
+
 }
index 31742fbecd945ffb7783e7f87e71edd9fdc239c1..e957723d35f43ee5fe8b85515a46f4a2e569a78b 100644 (file)
@@ -123,4 +123,6 @@ public abstract class SSAStatement implements Printable {
     }
 
     public abstract void forValRefs(ValRefVisitor visitor);
+
+    public abstract void cleanup();
 }
index 296ec6d1f461545c72222ff8ad49cde1310ef267..4eca7d1aeb35fa851124281175643a0f489025e7 100644 (file)
@@ -223,4 +223,9 @@ public class If extends SSAExit implements ValRefBinder {
     public void forValRefs(ValRefVisitor visitor) {
         visitor.visit(condition);
     }
+
+    @Override
+    public void cleanup() {
+        condition.remove();
+    }
 }
index 6ffbe88619e1e05cf331950e50a3b3c5d1d5b1cb..eb1a3dea972dc61f05914b4c18e13015b37adf63 100644 (file)
@@ -146,4 +146,10 @@ public class Jump extends SSAExit implements ValRefBinder {
         for(ValRef parameter : parameters)
             visitor.visit(parameter);
     }
+
+    @Override
+    public void cleanup() {
+        for(ValRef parameter : parameters)
+            parameter.remove();
+    }
 }
index 386199d838d3cfd0e60cb1faea0375bb45791e21..b3584ae7e53692c5196bcf0f9f575723296f5117 100644 (file)
@@ -279,4 +279,9 @@ public class Switch extends SSAExit implements ValRefBinder {
     public void forValRefs(ValRefVisitor visitor) {
         visitor.visit(scrutinee);
     }
+
+    @Override
+    public void cleanup() {
+        scrutinee.remove();
+    }
 }
index b0391b96581cbe0df97d498589a862a23fe8b775..593b81c63f2914c40355e47b7932336f3e0ced29 100644 (file)
@@ -93,4 +93,8 @@ public class Throw extends SSAExit {
     @Override
     public void forValRefs(ValRefVisitor visitor) {
     }
+
+    @Override
+    public void cleanup() {
+    }
 }
index 400cb6e4e373f78c480e8fc37c1da3dc203a47f2..d9d79737f5222ab39e475a6385377eef49f8f0ce 100644 (file)
@@ -488,4 +488,11 @@ public class LetApply extends LetStatement implements ValRefBinder {
         for(ValRef parameter : parameters)
             visitor.visit(parameter);
     }
+
+    @Override
+    public void cleanup() {
+        function.remove();
+        for(ValRef parameter : parameters)
+            parameter.remove();
+    }
 }
index 9f6fe9f01d8511bf86e6f49f740a9795e6f16531..dc6a8580d713cc8c579ef1cbdfb45aad24dd34ea 100644 (file)
@@ -275,4 +275,10 @@ public class LetFunctions extends SSAStatement implements ClosureBinder {
         for(SSAClosure closure = firstClosure; closure != null; closure = closure.getNext())
             closure.forValRefs(visitor);    
     }
+
+    @Override
+    public void cleanup() {
+        for(SSAClosure closure = firstClosure; closure != null; closure = closure.getNext())
+            closure.cleanup();
+    }
 }
index a1724428303502e3de48030bf7545e853735ce3f..67f43838d837ff897d8a0f8c9a3d1e97df13ecee 100644 (file)
@@ -430,6 +430,7 @@ public class ExpressionEvaluator {
             throw new SCLExpressionCompilationException(errorLog.getErrors());
         }
         Map<String, byte[]> classes = moduleBuilder.getClasses();
+        ssaModule.cleanup();
         
         // Load generated code and execute
         try {
diff --git a/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/MemoryLeakTest.java b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/MemoryLeakTest.java
new file mode 100644 (file)
index 0000000..7ff61a8
--- /dev/null
@@ -0,0 +1,74 @@
+package org.simantics.scl.compiler.tests;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification;
+import org.simantics.scl.compiler.errors.CompilationErrorFormatter;
+import org.simantics.scl.compiler.module.repository.ImportFailure;
+import org.simantics.scl.compiler.module.repository.ImportFailureException;
+import org.simantics.scl.compiler.module.repository.ModuleRepository;
+import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
+import org.simantics.scl.compiler.top.ExpressionEvaluator;
+import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
+import org.simantics.scl.compiler.types.Type;
+import org.simantics.scl.compiler.types.Types;
+
+public class MemoryLeakTest {
+    ModuleRepository moduleRepository;
+
+    EnvironmentSpecification environmentSpecification;
+
+    @Before
+    public void initialize() throws Exception {
+        moduleRepository = InitialRepository.getInitialRepository();
+
+        // Environment for compiling expressions
+        environmentSpecification = new EnvironmentSpecification();
+        environmentSpecification.importModule("Builtin", "");
+        environmentSpecification.importModule("Prelude", "");
+    }
+
+    private void testExpression0(String expressionText,
+            Object expectedValue,
+            Type expectedType) throws Exception {
+        
+        RuntimeEnvironment runtimeEnvironment;
+        try {
+            runtimeEnvironment = moduleRepository.createRuntimeEnvironment(environmentSpecification,
+                    getClass().getClassLoader());
+        } catch(ImportFailureException e) {
+            for(ImportFailure failure : e.failures)
+                System.err.println("Failed to import " + failure.moduleName);
+            throw e;
+        }
+
+        // Compiling and running expression
+        try {
+            Object result = new ExpressionEvaluator(runtimeEnvironment, expressionText)
+                    .expectedType(expectedType)
+                    .interpretIfPossible(false)
+                    .eval();
+            if(expectedValue != null)
+                Assert.assertEquals(expectedValue, result);
+        } catch(SCLExpressionCompilationException e) {
+            System.out.println(CompilationErrorFormatter.toString(expressionText, e.getErrors()));
+            throw e;
+        }
+    }
+
+    @Test
+    public void testIt() throws Exception {
+        for(int i=0;i<1000000;++i) {
+            testExpression0("fst (\"a\", \"b\")", "a", Types.STRING);
+            if(i % 10000 == 0 && i > 0) {
+                System.gc();
+                System.runFinalization();
+                System.gc();
+                Thread.sleep(100L);
+                double used = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
+                System.out.println(i + " " + used*1e-6 + " Mb, " + (used / i) + " b / expression");
+            }
+        }
+    }
+}