我創建了一個類JavaRunner
,它從字符串動態地創建一個文件,將其編譯到內存中並運行它的主要方法(我還創建了一個寫入文件並將其編譯到磁盤上的方法結果相似)。如何從內存中清除動態編譯的類
我創建了2個其他類,稱爲跑步者。
第一個是TerminalRunner
,它將類名和源作爲參數並調用JavaRunner.compile,這很好,因爲它每次調用它時只運行一次。
第二類是RunnerServlet
,它啓動一個小的java服務器,它接收一個使用JavaRunner編寫的post請求並運行代碼並返回一個帶有sys.out和sys.err流的JSON對象。
如果我發佈{name:「Main」,代碼:「[某些Java代碼]」}我得到正確的響應;然而,如果我用不同的源代碼調用同一個Main類,我會得到第一個結果。
我追蹤了代碼,並將源字符串正確傳遞給JavaCompiler。 這個問題與編譯的類有關,我猜它是由JVM以某種方式緩存的。
這是JavaRunner.java
public static void compile(String name, String code, int timeLimit){
/*Creating dynamic java source code file object*/
SimpleJavaFileObject fileObject = new DynamicJavaSourceCodeObject (name, code) ;
JavaFileObject javaFileObjects[] = new JavaFileObject[]{fileObject} ;
/*Instantiating the java compiler*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/**
* Retrieving the standard file manager from compiler object, which is used to provide
* basic building block for customizing how a compiler reads and writes to files.
*
* The same file manager can be reopened for another compiler task.
* Thus we reduce the overhead of scanning through file system and jar files each time
*/
StandardJavaFileManager stdFileManager = compiler.getStandardFileManager(null, null, null);
try {
stdFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File("./temp")));
} catch (IOException e) {
e.printStackTrace();
}
/* Prepare a list of compilation units (java source code file objects) to input to compilation task*/
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(javaFileObjects);
/*Prepare any compilation options to be used during compilation*/
//In this example, we are asking the compiler to place the output files under bin folder.
List<String> compileOptions = new ArrayList<String>();
// compileOptions.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
// Iterable<String> compilationOptionss = Arrays.asList(compileOptions);
/*Create a diagnostic controller, which holds the compilation problems*/
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
/*Create a compilation task from compiler by passing in the required input objects prepared above*/
CompilationTask compilerTask = compiler.getTask(null, stdFileManager, diagnostics, compileOptions, null, compilationUnits) ;
//Perform the compilation by calling the call method on compilerTask object.
boolean status = compilerTask.call();
if (!status){//If compilation error occurs
/*Iterate through each compilation problem and print it*/
for (Diagnostic diagnostic : diagnostics.getDiagnostics()){
System.err.format("Error on line %d in %s", diagnostic.getLineNumber(), diagnostic);
}
} else {
// ExecutorService service = Executors.newSingleThreadExecutor();
// try {
// Runnable r = new Runnable() {
// @Override
// public void run() {
try {
Class.forName(name).getDeclaredMethod("main", new Class[] { String[].class }).invoke(null, new Object[] { null });
} catch (ClassNotFoundException e) {
System.err.println("Class not found: " + e);
} catch (NoSuchMethodException e) {
System.err.println("No such method: " + e);
} catch (IllegalAccessException e) {
System.err.println("Illegal access: " + e);
} catch (InvocationTargetException e) {
System.err.println("RuntimeError: "+e.getTargetException());
}
// }
// };
// Future<?> f = service.submit(r);
// f.get(timeLimit, TimeUnit.MILLISECONDS); // attempt the task for timelimit default 5 seconds
// }
// catch (final InterruptedException e) {
// System.err.println("Thread Interrupted: " + e);
// }
// catch (final TimeoutException e) {
// System.err.println("TimeoutException: Your program ran for more than "+timeLimit);
// }
// catch (final ExecutionException e) {
// e.printStackTrace();
// }
// finally {
// service.shutdown();
// }
}
try {
(new File("./temp/"+name+".class")).delete();
stdFileManager.close() ;//Close the file manager
} catch (IOException e) {
e.printStackTrace();
}
}
compile方法這是DynaDynamicJavaSourceCodeObject
class DynamicJavaSourceCodeObject extends SimpleJavaFileObject{
private String sourceCode ;
/**
* Converts the name to an URI, as that is the format expected by JavaFileObject
*
*
* @param String name given to the class file
* @param String source the source code string
*/
protected DynamicJavaSourceCodeObject(String name, String source) {
super(URI.create("string:///" +name.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
this.sourceCode = source ;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
throws IOException {
return sourceCode ;
}
public String getSourceCode() {
return sourceCode;
}
}
有什麼建議?
到目前爲止,我設置CLASS_OUPUT爲/temp
目錄,我刪除它們 然而,一旦一個類被定義即使我刪除它,它仍保留在內存的某個地方
有沒有辦法從Java的內存中清除類?
我創建了一個回購與我目前的進展here
我的解決辦法,如果一切都失敗了,是生成隨機文件名的時候,每10000彙編我會重新啓動服務器或東西(但它是凌亂)
請參閱[在java中卸載類?](http://stackoverflow.com/questions/148681/unloading-classes-in-java)瞭解一些選項。 – 2014-10-11 00:24:39
似乎很有趣 – 2014-10-11 00:26:18
您必須創建一個類加載器,使用該類加載器加載該類,然後在完成後清空所有對類和類加載器的引用。 (包括引用類的任何類,請注意,這意味着你必須使用「工廠」來創建類的實例。) – 2014-10-11 00:44:33