From 7e3061cfff1ac4f100fd671feccea25841222cb0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Hannu=20Niemist=C3=B6?= Date: Tue, 4 Jul 2017 12:44:40 +0300 Subject: [PATCH] Added memory leak test and fixed the leak by removing references refs #7352 Change-Id: Idc412759d49314f3226e6b1648881aaab8248fe9 --- .../scl/compiler/constants/SCLConstant.java | 7 ++ .../internal/codegen/ssa/SSABlock.java | 7 ++ .../internal/codegen/ssa/SSAClosure.java | 2 + .../internal/codegen/ssa/SSAExit.java | 2 + .../internal/codegen/ssa/SSAFunction.java | 7 +- .../internal/codegen/ssa/SSAModule.java | 7 ++ .../internal/codegen/ssa/SSAObject.java | 6 ++ .../internal/codegen/ssa/SSAStatement.java | 2 + .../internal/codegen/ssa/exits/If.java | 5 ++ .../internal/codegen/ssa/exits/Jump.java | 6 ++ .../internal/codegen/ssa/exits/Switch.java | 5 ++ .../internal/codegen/ssa/exits/Throw.java | 4 + .../codegen/ssa/statements/LetApply.java | 7 ++ .../codegen/ssa/statements/LetFunctions.java | 6 ++ .../scl/compiler/top/ExpressionEvaluator.java | 1 + .../scl/compiler/tests/MemoryLeakTest.java | 74 +++++++++++++++++++ 16 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/MemoryLeakTest.java diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/SCLConstant.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/SCLConstant.java index 14bcbcaf6..9089ebb81 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/SCLConstant.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/SCLConstant.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSABlock.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSABlock.java index ba9d62d27..60e156a7f 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSABlock.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSABlock.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAClosure.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAClosure.java index 57748b284..3dd77d926 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAClosure.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAClosure.java @@ -104,5 +104,7 @@ public abstract class SSAClosure implements Printable, BoundVarBinder { } public abstract void forValRefs(ValRefVisitor visitor); + + public abstract void cleanup(); } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAExit.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAExit.java index c183699f9..ae9ad5725 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAExit.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAExit.java @@ -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 diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAFunction.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAFunction.java index b9e8bfbb3..209c92a64 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAFunction.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAFunction.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAModule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAModule.java index e0dbf214f..c9c8b35fb 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAModule.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAModule.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAObject.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAObject.java index 56877e304..a5ce6f95b 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAObject.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAObject.java @@ -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(); + } + } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAStatement.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAStatement.java index 31742fbec..e957723d3 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAStatement.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/SSAStatement.java @@ -123,4 +123,6 @@ public abstract class SSAStatement implements Printable { } public abstract void forValRefs(ValRefVisitor visitor); + + public abstract void cleanup(); } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/If.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/If.java index 296ec6d1f..4eca7d1ae 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/If.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/If.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Jump.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Jump.java index 6ffbe8861..eb1a3dea9 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Jump.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Jump.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java index 386199d83..b3584ae7e 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Switch.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Throw.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Throw.java index b0391b965..593b81c63 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Throw.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/exits/Throw.java @@ -93,4 +93,8 @@ public class Throw extends SSAExit { @Override public void forValRefs(ValRefVisitor visitor) { } + + @Override + public void cleanup() { + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java index 400cb6e4e..d9d79737f 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetApply.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java index 9f6fe9f01..dc6a8580d 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/ssa/statements/LetFunctions.java @@ -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(); + } } diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java index a17244283..67f43838d 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java @@ -430,6 +430,7 @@ public class ExpressionEvaluator { throw new SCLExpressionCompilationException(errorLog.getErrors()); } Map 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 index 000000000..7ff61a83e --- /dev/null +++ b/tests/org.simantics.scl.compiler.tests/src/org/simantics/scl/compiler/tests/MemoryLeakTest.java @@ -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"); + } + } + } +} -- 2.43.2