Skip to content

Commit

Permalink
GROOVY-7683 - Memory leak when using Groovy as JSR-223 scripting lang…
Browse files Browse the repository at this point in the history
…uage
  • Loading branch information
jwagenleitner committed Jun 27, 2016
1 parent a55b77b commit f7c688e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 41 deletions.
84 changes: 43 additions & 41 deletions src/main/org/codehaus/groovy/reflection/ClassInfo.java
Expand Up @@ -25,23 +25,31 @@
import org.codehaus.groovy.util.*;
import org.codehaus.groovy.vmplugin.VMPluginFactory;

import java.lang.ref.WeakReference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Handle for all information we want to keep about the class
* <p>
* This class handles caching internally and its advisable to not store
* references directly to objects of this class. The static factory method
* {@link ClassInfo#getClassInfo(Class)} should be used to retrieve an instance
* from the cache. Internally the {@code Class} associated with a {@code ClassInfo}
* instance is kept as {@link WeakReference}, so it not safe to reference
* and instance without the Class being either strongly or softly reachable.
*
* @author Alex.Tkachman
*/
public class ClassInfo {
public class ClassInfo implements Finalizable {

private final LazyCachedClassRef cachedClassRef;
private final LazyClassLoaderRef artifactClassLoader;
private final LockableObject lock = new LockableObject();
public final int hash = -1;
private final Class klazz;
private final WeakReference<Class<?>> klazz;

private final AtomicInteger version = new AtomicInteger();

Expand All @@ -68,11 +76,7 @@ public ClassInfo computeValue(Class<?> type) {
private static final GlobalClassSet globalClassSet = new GlobalClassSet();

ClassInfo(Class klazz) {
this.klazz = klazz;
if (ClassInfo.DebugRef.debug)
new DebugRef(klazz);
new ClassInfoCleanup(this);

this.klazz = new WeakReference<Class<?>>(klazz);
cachedClassRef = new LazyCachedClassRef(softBundle, this);
artifactClassLoader = new LazyClassLoaderRef(softBundle, this);
}
Expand Down Expand Up @@ -103,6 +107,19 @@ public static void clearModifiedExpandos() {
}
}

/**
* Returns the {@code Class} associated with this {@code ClassInfo}.
* <p>
* This method can return {@code null} if the {@code Class} is no longer reachable
* through any strong or soft references. A non-null return value indicates that this
* {@code ClassInfo} is valid.
*
* @return the {@code Class} associated with this {@code ClassInfo}, else {@code null}
*/
public final Class<?> getTheClass() {
return klazz.get();
}

public CachedClass getCachedClass() {
return cachedClassRef.get();
}
Expand Down Expand Up @@ -222,7 +239,7 @@ private MetaClass getMetaClassUnderLock() {
return answer;
}

answer = mccHandle.create(klazz, metaClassRegistry);
answer = mccHandle.create(klazz.get(), metaClassRegistry);
answer.initialize();

if (GroovySystem.isKeepJavaMetaClasses()) {
Expand All @@ -248,6 +265,17 @@ private static boolean isValidWeakMetaClass(MetaClass metaClass, MetaClassRegist
return (!enableGloballyOn || cachedAnswerIsEMC);
}

/**
* Returns the {@code MetaClass} for the {@code Class} associated with this {@code ClassInfo}.
* If no {@code MetaClass} exists one will be created.
* <p>
* It is not safe to call this method without a {@code Class} associated with this {@code ClassInfo}.
* It is advisable to aways retrieve a ClassInfo instance from the cache by using the static
* factory method {@link ClassInfo#getClassInfo(Class)} to ensure the referenced Class is
* strongly reachable.
*
* @return a {@code MetaClass} instance
*/
public final MetaClass getMetaClass() {
MetaClass answer = getMetaClassForClass();
if (answer != null) return answer;
Expand Down Expand Up @@ -375,7 +403,7 @@ private static class LazyCachedClassRef extends LazyReference<CachedClass> {
}

public CachedClass initValue() {
return createCachedClass(info.klazz, info);
return createCachedClass(info.klazz.get(), info);
}
}

Expand All @@ -388,43 +416,17 @@ private static class LazyClassLoaderRef extends LazyReference<ClassLoaderForClas
}

public ClassLoaderForClassArtifacts initValue() {
return new ClassLoaderForClassArtifacts(info.klazz);
return new ClassLoaderForClassArtifacts(info.klazz.get());
}
}

private static class ClassInfoCleanup extends ManagedReference<ClassInfo> {

public ClassInfoCleanup(ClassInfo classInfo) {
super(weakBundle, classInfo);
}

public void finalizeRef() {
ClassInfo classInfo = get();
classInfo.setStrongMetaClass(null);
classInfo.cachedClassRef.clear();
classInfo.artifactClassLoader.clear();
}
@Override
public void finalizeReference() {
setStrongMetaClass(null);
cachedClassRef.clear();
artifactClassLoader.clear();
}

private static class DebugRef extends ManagedReference<Class> {
public static final boolean debug = false;

private static final AtomicInteger count = new AtomicInteger();

final String name;

public DebugRef(Class klazz) {
super(softBundle, klazz);
name = klazz == null ? "<null>" : klazz.getName();
count.incrementAndGet();
}

public void finalizeRef() {
//System.out.println(name + " unloaded " + count.decrementAndGet() + " classes kept");
super.finalizeReference();
}
}

private static class GlobalClassSet {

private final ManagedLinkedList<ClassInfo> items = new ManagedLinkedList<ClassInfo>(weakBundle);
Expand Down
Expand Up @@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.reflection;

import org.codehaus.groovy.util.Finalizable;
import org.codehaus.groovy.util.ManagedConcurrentMap;
import org.codehaus.groovy.util.ReferenceBundle;

Expand All @@ -40,6 +41,15 @@ public EntryWithValue(GroovyClassValuePreJava7Segment segment, Class<?> key, int
public void setValue(T value) {
if(value!=null) super.setValue(value);
}

@Override
public void finalizeReference() {
T value = getValue();
if (value instanceof Finalizable) {
((Finalizable) value).finalizeReference();
}
super.finalizeReference();
}
}

private class GroovyClassValuePreJava7Segment extends ManagedConcurrentMap.Segment<Class<?>,T> {
Expand Down

0 comments on commit f7c688e

Please sign in to comment.