我在一段用于在运行时编译和运行 Java 代码的代码中检测到严重的内存泄漏。我已经创建了堆转储,看来 com.sun.tools.javac.util.SharedNameTable$NameImpL
是罪魁祸首。
我想知道如何防止 SharedNameTable
占用如此多的空间。有没有办法强制释放 SharedNameTable
?
编译器代码:
public static Object compile(String code) throws IOException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, java.lang.InstantiationException {
String uuid = UUID.randomUUID().toString();
File theDir = new File(uuid);
if (!theDir.exists()) {
System.out.println("creating directory: " + uuid);
boolean result = false;
try{
theDir.mkdir();
result = true;
}
catch(SecurityException se){
System.out.println(se);
}
if(result) {
System.out.println("DIR created");
}
}
//Compile
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm = jc.getStandardFileManager(null, null, null);
JavaFileObject javaObjectFromString = getJavaFileContentsAsString(new StringBuilder(code));
System.out.println(javaObjectFromString.getName());
Iterable fileObjects = Arrays.asList(javaObjectFromString);
String[] options = new String[]{"-d", "/src/"+uuid};
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
if (jc.getTask(null, null, diagnostics, Arrays.asList(options), null, fileObjects).call()) {
sjfm.close();
System.out.println("Class has been successfully compiled");
//Load
URL[] urls = new URL[]{new URL("file:///src/"+uuid+"/")};
URLClassLoader ucl = new URLClassLoader(urls);
Class cl = ucl.loadClass("TestClass");
System.out.println("Class has been successfully loaded");
//Run
final Method method = cl.getDeclaredMethod("testMethod");
final Object object = cl.newInstance();
ExecutorService executor = Executors.newFixedThreadPool(1);
Callable<Object> task = new Callable<Object>() {
public Object call() throws IllegalAccessException, InvocationTargetException {
return method.invoke(object);
}
};
Future<Object> future = executor.submit(task);
try {
Object result = future.get(20, TimeUnit.SECONDS);
return result;
} catch (TimeoutException ex) {
return "Method timed out (20 seconds). Please review your code.";
} catch (InterruptedException e) {
return "Method interrupted.";
} catch (ExecutionException e) {
e.printStackTrace();
return String.format("ExecutionException thrown! %s", e.getMessage());
} finally {
future.cancel(true);
executor.shutdown();
}
}
sjfm.close();
System.out.println("Class compilation failed!");
//Create diagnostics and return them
List<String> errors = new ArrayList<>();
for (Diagnostic diagnostic : diagnostics.getDiagnostics()){
String s = String.format("[Code:%s] %s on line %d / column % d in %s",
diagnostic.getCode(),
diagnostic.getKind().toString(),
diagnostic.getLineNumber(),
diagnostic.getColumnNumber(),
diagnostic.getSource());
errors.add(s);
errors.add(diagnostic.toString());
}
return errors.toArray();
}
编辑:
我找到了similar question其中指出 SoftReference 是问题的原因。强制发生 OutOfMemoryException 应该强制清除这些异常。不过,我使用“node-java”npm 包在 node.js 应用程序中运行此代码。当我尝试强制 OoM 时,我的 node.js 应用程序将在抛出异常之前被终止。
最佳答案
正如我在 my answer 到 this question 中所写的那样,有一个编译器选项可以完全阻止使用 SharedNameTable
。您可以将其添加为附加编译器选项:
String[] options = new String[]{"-d", "/src/"+uuid, "-XDuseUnsharedTable"};
关于java - 使用编译器 API 的程序中的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33548218/