2015-11-05 16 views
3

我在一段代码中检测到严重的内存泄漏,该代码用于在运行时编译和运行Java代码。我已经创建堆转储,似乎是com.sun.tools.javac.util.SharedNameTable$NameImpL是罪魁祸首。程序中使用编译器API的内存泄漏

我想知道的是我如何防止SharedNameTable占用这么多空间。有没有办法强制释放SharedNameTable

enter image description here

编译器代码:

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 s的指出是问题的原因。强制OutOfMemoryException应该强制清除这些。不过,我使用'node-java'npm包在node.js应用程序中运行此代码。当我尝试强制一个OoM时,我的node.js应用程序将在抛出异常之前被杀死。

+0

什么发生,如果你离开,如果statment空只是调用'jc.getTask(NULL,NULL,诊断,Arrays.asList(选项),空,fileObjects) .call()'? –

+0

对不起,我不确定我是否正确理解了您的建议。我尝试在if语句之外运行'jc.getTask(null,null,diagnostics,Arrays.asList(options),null,fileObjects).call()',并且它没有导致内存消耗。 – Hartger

+0

所以编译任务不是问题。 –

回答

0

正如我在my answerthis question中所写的那样,有一个编译器选项可以完全阻止使用SharedNameTable。您可以只需添加它作为一个额外的编译器选项:

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