英文:
Why my code leaking memory after many function calls? Is ClassLoader a problem?
问题
我有一个名为getAllTests()
的方法,它加载一个文件夹中的外部JAR文件,然后使用反射搜索带有@Test注解的方法。我使用以下代码:
- 使用
URLClassLoader
类来加载JAR文件; - 使用
Class currentClass = child.loadClass(classname);
来加载类; - 使用
Method method = currentClass.getMethods()[i];
来获取方法; - 使用
Annotation annTest = method.getAnnotation(Test.class);
来获取注解。
我的代码如下:
public static void getAllTests() throws IllegalArgumentException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, LinkageError, ClassNotFoundException {
TestLoaderApplication.testClassObjMap.clear(); // 这是一个HashMap类
LoadLibrary loadLibrary = new LoadLibrary(); // 用于搜索文件夹中的所有.jar文件
List<JarFile> jarList = loadLibrary.getListJar(pathJars).stream().map(f -> {
try {
return new JarFile(f);
} catch (IOException e) {
e.printStackTrace();
}
return null;
})
.collect(Collectors.toList());
for (JarFile j : jarList) {
URLClassLoader child = new URLClassLoader(
new URL[] { new File(j.getName()).toURI().toURL() },
ServiceUtil.class.getClassLoader()
);
for (Enumeration<JarEntry> entries = j.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
String file = entry.getName();
if (file.endsWith(".class")) {
String classname = file.replaceAll("/", ".")
.substring(0, file.lastIndexOf("."));
try {
Class currentClass = child.loadClass(classname);
List<String> testMethods = new ArrayList<>();
for (int i = 0; i < currentClass.getMethods().length; i++) {
Method method = currentClass.getMethods()[i];
Annotation annTest = method.getAnnotation(Test.class);
Annotation annTestFactory = method.getAnnotation(TestFactory.class);
if (annTest != null || annTestFactory != null) {
testMethods.add(method.getName());
}
}
if (testMethods.size() >= 1) {
testClassObjMap.put(j.getName().substring(j.getName().lastIndexOf("\\") + 1), classname, testMethods);
TestLoaderApplication.testClassObjMap.put(j.getName().substring(j.getName().lastIndexOf("/") + 1), classname, testMethods);
LOGGER.info(String.format("%s %s %s", j.toString(), classname, testMethods));
}
} catch (NoClassDefFoundError e) {
LOGGER.warn("WARNING: failed NoClassDefFoundError " + classname + " from " + file);
} catch (Throwable e) {
LOGGER.warn("WARNING: failed to instantiate " + classname + " from " + file);
}
}
}
j.close();
child.close();
child = null;
}
System.gc();
LOGGER.info("Test Loader Console Back-End: Fine Reflection Scan");
}
在这个函数的末尾,我关闭了JarFile(j)
和classLoader(child)
,并将child
设置为null
,然后调用了垃圾回收器。
但是,如果我多次调用这个方法,最终Java Web应用程序的内存使用率会达到约100%。
这个方法位于一个Spring Boot项目中,我使用Tomcat作为Web服务器。
你能否推荐一些在使用类加载器方面的优化方法,以及这是否是查找Java中带有@Test注解的方法的正确方式?谢谢。
英文:
i have a mehod getAllTests() that load external jar in a folder and with Reflection search method with Annotation Test (@Test).
I use:
URLClassLoader
class for load Jar File;
Class currentClass=child.loadClass(classname);
for load class;
Method method = currentClass.getMethods()[i];
Annotation annTest = method.getAnnotation(Test.class);
for get Method and get Annotation.
My code is this:
public static void getAllTests() throws IllegalArgumentException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException,LinkageError, ClassNotFoundException {
TestLoaderApplication.testClassObjMap.clear(); <--is a HashMap class
LoadLibrary loadLibrary=new LoadLibrary();<--used for search all file .jar in folder
List<JarFile> jarList= loadLibrary.getListJar(pathJars).stream().map(f -> {
try {
return new JarFile(f);
} catch (IOException e) {
e.printStackTrace();
}
return null;
})
.collect(Collectors.toList());
for (JarFile j : jarList) {
URLClassLoader child = new URLClassLoader(
new URL[] {new File(j.getName()).toURI().toURL()},
ServiceUtil.class.getClassLoader()
);
for (Enumeration<JarEntry> entries = j.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
String file = entry.getName();
if (file.endsWith(".class")) {
String classname = file.replaceAll("/", ".")
.substring(0, file.lastIndexOf("."));
try {
Class currentClass=child.loadClass(classname);
List<String> testMethods = new ArrayList<>();
for (int i = 0; i < currentClass.getMethods().length; i++) {
Method method = currentClass.getMethods()[i];
Annotation annTest = method.getAnnotation(Test.class);
Annotation annTestFactory = method.getAnnotation(TestFactory.class);
if (annTest != null || annTestFactory != null) {
testMethods.add(method.getName());
}
}//fine for metodi
if (testMethods.size() >=1) {
testClassObjMap.put(j.getName().substring(j.getName().lastIndexOf("\\")+1),classname,testMethods);
TestLoaderApplication.testClassObjMap.put(j.getName().substring(j.getName().lastIndexOf("/")+1),classname,testMethods);
LOGGER.info(String.format("%s %s %s",j.toString(),classname,testMethods));
}
}catch (NoClassDefFoundError e) {
LOGGER.warn("WARNING: failed NoClassDefFoundError " + classname + " from " + file);
}
catch (Throwable e) {
LOGGER.warn("WARNING: failed to instantiate " + classname + " from " + file);
}
}//if .class
}//chiudo jarentry for
j.close();
child.close();
child=null;
}//chiudo jarfile for
System.gc();
LOGGER.info("Test Loader Console Back-End:\tFine Reflection Scan");
}
At end of this function i close both JarFile(j) and classLoader(child) and set child=null and agter call garbage collector.
But if i call this method many times at end i have about 100% memory used by java webapps.
This method is in a spring-boot project and i use Tomcat for webserver.
Can you recommend some optimization in the use of the classLoader and if this is the correct way to look for methods with annotation @Test in Java?
Thanks
专注分享java语言的经验与见解,让所有开发者获益!
评论