Java 使用编译器API的程序内存泄漏

Java 使用编译器API的程序内存泄漏,java,memory-leaks,heap-memory,java-compiler-api,Java,Memory Leaks,Heap Memory,Java Compiler Api,我在一段用于在运行时编译和运行Java代码的代码中检测到严重的内存泄漏。我创建了堆转储,似乎是com.sun.tools.javac.util.SharedNameTable$NameImpL造成的 我想知道的是如何防止SharedNameTable占用这么多空间。有没有办法强制释放共享名称表 编译器代码: public static Object compile(String code) throws IOException, InvocationTargetException, Illeg

我在一段用于在运行时编译和运行Java代码的代码中检测到严重的内存泄漏。我创建了堆转储,似乎是
com.sun.tools.javac.util.SharedNameTable$NameImpL
造成的

我想知道的是如何防止
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();
}
publicstaticobjectcompile(字符串代码)抛出IOException、InvocationTargetException、IllegalAccessException、ClassNotFoundException、NoSuchMethodException、java.lang.InstantiationException{
字符串uuid=uuid.randomUUID().toString();
File theDir=新文件(uuid);
如果(!theDir.exists()){
System.out.println(“创建目录:+uuid”);
布尔结果=假;
试一试{
theDir.mkdir();
结果=真;
}
捕获(安全异常se){
系统输出打印LN(se);
}
如果(结果){
System.out.println(“创建目录”);
}
}
//编撰
JavaCompiler jc=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager sjfm=jc.getStandardFileManager(null,null,null);
JavaFileObject javaObjectFromString=GetJavaFileContentsString(新的StringBuilder(代码));
System.out.println(javaObjectFromString.getName());
Iterable fileObjects=Arrays.asList(javaObjectFromString);
字符串[]选项=新字符串[]{“-d”,“/src/”+uuid};
DiagnosticCollector diagnostics=新建DiagnosticCollector();
if(jc.getTask(null、null、诊断、Arrays.asList(options)、null、fileObjects.call()){
sjfm.close();
System.out.println(“类已成功编译”);
//装载
URL[]URL=新URL[]{新URL(“file:///src/“+uuid+”/”)};
URLClassLoader ucl=新的URLClassLoader(URL);
类别cl=ucl.loadClass(“TestClass”);
System.out.println(“类已成功加载”);
//跑
最终方法=cl.getDeclaredMethod(“测试方法”);
最终对象=cl.newInstance();
ExecutorService executor=Executors.newFixedThreadPool(1);
可调用任务=新的可调用任务(){
公共对象调用()引发IllegalAccessException,InvocationTargetException{
返回方法。调用(对象);
}
};
未来=执行者提交(任务);
试一试{
对象结果=future.get(20,TimeUnit.SECONDS);
返回结果;
}捕获(TimeoutException例外){
return“方法超时(20秒)。请检查您的代码。”;
}捕捉(中断异常e){
返回“方法中断。”;
}捕获(执行例外){
e、 printStackTrace();
返回String.format(“抛出ExecutionException!%s”,e.getMessage());
}最后{
future.cancel(true);
executor.shutdown();
}
}
sjfm.close();
System.out.println(“类编译失败!”);
//创建诊断并返回它们
列表错误=新建ArrayList();
用于(诊断:diagnostics.getDiagnostics()){
String s=String.format(“[代码:%s]%s,第%d行/第%d列,在%s中”,
diagnostic.getCode(),
diagnostic.getKind().toString(),
diagnostic.getLineNumber(),
diagnostic.getColumnNumber(),
diagnostic.getSource());
错误。添加;
errors.add(diagnostic.toString());
}
返回错误。toArray();
}
编辑:

我发现一个
SoftReference
s被指出是问题的原因。强制执行OutOfMemoryException应该强制清除这些异常。但是,我正在node.js应用程序中使用'node java'npm包运行这段代码。当我尝试强制OoM时,我的node.js应用程序将在引发异常之前被终止。

正如我在中写到的,有一个编译器选项可以阻止使用
SharedNameTable
。您只需将其作为附加编译器选项添加即可:

String[] options = new String[]{"-d", "/src/"+uuid, "-XDuseUnsharedTable"};

如果只调用
jc.getTask(null,null,diagnostics,Arrays.asList(options),null,fileObjects)。call()
,而将if语句保留为空,会发生什么情况?对不起,我不确定我是否正确理解了您的建议。我尝试在if语句之外运行
jc.getTask(null,null,diagnostics,Arrays.asList(options),null,fileObjects).call()
,但没有内存消耗的结果。所以编译任务不是问题。这真的是内存泄漏吗?如果需要释放内存,将收集软引用。在释放内存之前,Node.JS似乎会失败。我在Java中发现了一个内存泄漏,与我正在经历的内存泄漏完全相同(
SharedNameTable
ZipFileIndex
在使用
javax.tools.JavaCompiler$CompilationTask.call时,其大小会增加)。