/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.codehaus.groovy.reflection;
import groovy.lang.GroovyClassLoader;
import org.apache.groovy.stress.util.GCUtils;
import org.codehaus.groovy.util.ReferenceBundle;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.groovy.util.ReferenceManager;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class ClassInfoLeakStressTest {
private static final int NUM_OBJECTS = 3101;
private static ReferenceBundle bundle = ReferenceBundle.getWeakBundle();
private ReferenceQueue<ClassLoader> classLoaderQueue = new ReferenceQueue<ClassLoader>();
private ReferenceQueue<Class<?>> classQueue = new ReferenceQueue<Class<?>>();
private ReferenceQueue<ClassInfo> classInfoQueue = new ReferenceQueue<ClassInfo>();
// Used to keep a hard reference to the References so they are not collected
private List<Reference<?>> refList = new ArrayList<Reference<?>>(NUM_OBJECTS * 3);
@Before
public void setUp() {
// Make sure we switch over to callback manager
ReferenceManager manager = bundle.getManager();
for (int i = 0; i < 1501; i++) {
manager.afterReferenceCreation(null);
}
}
@Test
public void testLeak() {
assertFalse(Boolean.getBoolean("groovy.use.classvalue"));
for (int i = 0; i < NUM_OBJECTS; i++) {
GroovyClassLoader gcl = new GroovyClassLoader();
Class scriptClass = gcl.parseClass("int myvar = " + i);
ClassInfo ci = ClassInfo.getClassInfo(scriptClass);
Reference<ClassLoader> classLoaderRef = new WeakReference<ClassLoader>(gcl, classLoaderQueue);
Reference<Class<?>> classRef = new WeakReference<Class<?>>(scriptClass, classQueue);
Reference<ClassInfo> classInfoRef = new WeakReference<ClassInfo>(ci, classInfoQueue);
refList.add(classLoaderRef);
refList.add(classRef);
refList.add(classInfoRef);
gcl = null;
scriptClass = null;
ci = null;
GCUtils.gc();
}
// Add new class to help evict the last collected entry
GroovyClassLoader gcl = new GroovyClassLoader();
Class scriptClass = gcl.parseClass("int myvar = 7777");
ClassInfo ci = ClassInfo.getClassInfo(scriptClass);
GCUtils.gc();
// All objects should have been collected
assertEquals("GroovyClassLoaders not collected by GC", NUM_OBJECTS, queueSize(classLoaderQueue));
assertEquals("Script Classes not collected by GC", NUM_OBJECTS, queueSize(classQueue));
int ciSize = queueSize(classInfoQueue);
assertEquals("ClassInfo objects [" + ciSize + "] collected by GC, expected [" + NUM_OBJECTS + "]",
NUM_OBJECTS, ciSize);
}
private int queueSize(ReferenceQueue<?> queue) {
int size = 0;
while (queue.poll() != null) {
++size;
}
return size;
}
}