Memory leak when using the JDK compiler at run time

I am trying to add javaeditor to my program to extend the program at runtime. Everything works fine, except when you use the program as a whole (I imitated 1000-10000 compilers). Memory usage is increasing and increasing, it seems that a memory leak is occurring.

In my program, the class is loaded, the constructor starts and the class is unloaded (no remaining instance and the Loader class become invalid because I set the pointer to zero). I analyzed the process using JConsole, classes are unloaded when the garbage collector runs.

I made a heapdum by opening it in a memory analyzer, the problem seems to be inside java.net.FactoryURLClassLoader (in the object com.sun.tools.javac.util.List). Since (com.sun.tools.javac) is part of the JDK, not the JRE, and SystemToolClassLoader is a FactoryURLClassLoader object, I would find a leak somewhere there. The number of loaded classes in SystemToolClassLoader increases from 1 to 521 the first time the compiler is started, but remains unchanged.

So, I have no idea where it is leaking, is there a way to reset SystemToolClassLoader? How could I pinpoint a leak.

EDIT: Well, I found out that this also happens in a very simple example. So it seems like this is part of the compilation, I don't need to load the class or instantiate it:

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;


public class Example {   

public static void main(String[] args)
{
    for (int i =0; i<10000;i++){
        try {
            System.out.println(i);
            compile();
        } catch (InstantiationException | IllegalAccessException
                | ClassNotFoundException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

public static void compile() throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException
{
    File source = new File( "src\\Example.java" ); // This File
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null );
    Iterable<? extends JavaFileObject> units;
    units = fileManager.getJavaFileObjectsFromFiles( Arrays.asList( source ) );
    compiler.getTask( null, fileManager, null, null, null, units ).call();
    fileManager.close();
}

}
+5
source share
5 answers
, ; , SoftReference .

Oracle JVM , . , .

, "" :

  • ScriptFileManager, GC-finalize-GC. .
  • 500 ''. โ€‹โ€‹
  • GC-finalize-GC. . .
  • 500 ''. โ€‹โ€‹
  • GC-finalize-GC. . .

() : Names (500 โ†’ 1k), SharedNameTable (500- > 1k), SharedNameTable$NameImpl ( ) [LSharedNameTable$NameImpl (500- > 1k).

EMA , SharedNameTable com.sun.tools.javac.util.List, , -, SoftReference SharedNameTable - ( , ). $NameImpl , . , , ... ?

, . , (-Xms32m -Xmx32m). , JVM SharedNameTable, OutOfMemoryError. :

-Xmx512m -Xms512m

Total memory: 477233152
Free memory: 331507232
Used memory: 138.97506713867188 MB
Loaded scripts: 500

Total memory: 489816064
Free memory: 203307408
Used memory: 273.23594665527344 MB
Loaded scripts: 1000

The classloader/component "java.net.FactoryURLClassLoader @ 0x8a8a748" occupies 279.709.192 (98,37%) bytes.

-Xmx32m -Xms32m

Total memory: 29687808
Free memory: 25017112
Used memory: 4.454322814941406 MB
Loaded scripts: 500

Total memory: 29884416
Free memory: 24702728
Used memory: 4.941642761230469 MB
Loaded scripts: 1000

One instance of "com.sun.tools.javac.file.ZipFileIndex" loaded by "java.net.FactoryURLClassLoader @ 0x8aa4cc8" occupies 2.230.736 (47,16%) bytes. The instance is referenced by *.*.script.ScriptFileManager @ 0x8ac8230.

( JDK.)

Script:

public class Avenger
{
    public Avenger()
    {
        JavaClassScriptCache.doNotCollect(this);
    }

    public static void main(String[] args)
    {
        // this method is called after compiling
        new Avenger();
    }
}

doNotCollect:

private static final int TO_LOAD = 1000;
private static final List<Object> _active = new ArrayList<Object>(TO_LOAD);

public static void doNotCollect(Object o)
{
    _active.add(o);
}

System.out.println("Loaded scripts: " + _active.size());
+5

, null. . JConsole , . .

, .

eclipse , com.sun.tools.javac.util.List Object, ...

, , ( ) . , , , , .

+2

, " , - , , ". -

( ) , , . , , JVM . , , .

+1

Java 7 : SharedNameTable, , , , , JVM - , , JVM -Xmx. Java 9. () , : -XDuseUnsharedTable.

+1

, , SoftReference SharedNameTable.

Chrispy mentioned the -XDuseUnsharedTablejavac option . So the last missing bit is how to enable this option when using the Java API:

compiler.getTask(null, fileManager, null, Arrays.asList("-XDuseUnsharedTable"), null, units)
0
source

All Articles