I have this code to generate class dynamically and load it
import javassist.CannotCompileException;
import javassist.ClassPool;
public class PermGenLeak {
private static final String PACKAGE_NAME = "com.jigarjoshi.permgenleak.";
public static void main(String[] args) throws CannotCompileException, InterruptedException {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
ClassPool pool = ClassPool.getDefault();
pool.makeClass(PACKAGE_NAME + i).toClass();
Thread.sleep(3);
}
}
}
I launched this class against Java 7 (jdk1.7.0_60) and as expected it filled up PermGenSpace and heap remained unused image shows permgen usage overtime and at the end JVM was terminated
Now the same code ran against Java 8 (jdk1.8.0_40-ea) and as expected it kept expanding native memory (Metaspace) but surprisingly for 1g of Metaspace it consumed 3g of Heap in OldGen (almost 3x of Metaspace maintained over time)
image shows Metaspace usage overtime and System memory usage sample
this email from Jon Masamitsu and this JEP ticket says
interned
String
and Class stats and some misc data has been moved to Heap
what exactly makes this increase in heap as it loads more classes into Metaspace ?
My hypothesis is that this is "ordinary" garbage that is being created by your example. I surmise that:
The
javaassist
code creates regular heap objects. They are mostly "big" and that causes them to be allocated directly into the OldGen heap. Or something else causes that.(UPDATE - looking at @apangin's Answer, I now suspect that they started out in the YoungGen heap and were tenured ...)
When
classLoader.defineClass
is called under the hood, it creates objects in metaspace from the byte array containing the classfile.The OldGen usage remains ... because nothing has triggered a full GC yet.
If you tweaked your example so that the classes were reachable, and then forced a full GC, I would expect (hope) to see the OldHeap usage to drop, indicating that it is "ordinary" garbage rather than a storage leak.
Run
jmap -histo PID
to see which objects consume the Heap Space.When I ran your example I saw the heap full of Javassist auxiliary objects: