Я обнаружил серьезную утечку памяти в куске кода, который используется для компиляции и запуска кода Java во время выполнения. Я создал кучи кучи, и кажется, что виновником является com.sun.tools.javac.util.SharedNameTable$NameImpL
.Утечка памяти в программе с использованием API-интерфейса компилятора
Что я хотел бы знать, так это то, как я могу предотвратить 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();
}
Edit:
Я нашел similar question где SoftReference
s укажут быть причиной проблемы. Принуждение к исключению OutOfMemoryException должно устранить их. Однако я запускаю этот код в приложении node.js, используя пакет «node-java» npm. Когда я пытаюсь заставить OoM, мое приложение node.js будет убито до того, как будет выброшено исключение.
Что произойдет, если вы оставите КРП Постулаты пустой просто вызов 'jc.getTask (NULL, NULL, диагностику, Arrays.asList (опции), нулевой fileObjects) .call() '?? –
Прошу прощения, я не уверен, правильно понял ваше предложение. Я попытался запустить 'jc.getTask (null, null, diagnostics, Arrays.asList (options), null, fileObjects) .call()' вне оператора if и не имел никакого результата при потреблении памяти. – Hartger
Так что задача компиляции - не проблема. –