Can somebody help me understand if what I see is deliberate, correct behaviour or some kind of leak in Java11? Let's take a stupid-simple hello world app:
package com.example;
public class HelloWorld {
public static void main(String[] args) throws InterruptedException {
for( int i =0 ; i < 50; i++){
Thread.sleep(1000);
System.out.println("hello " + i);
}
}
}
The only interesting part is a jar dependency. It can be any jar, but to make the problem more spectacular let's use a big one - old gwt-user jar, which weigh 30MB:
plugins {
id 'java'
}
group 'com.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/com.google.gwt/gwt-user
compile group: 'com.google.gwt', name: 'gwt-user', version: '2.7.0'
}
Run the app, open the jvisualvm, make a dump and look for retained set of java.util.zip.ZipFile$Source
:
That jar from classpath (never used, actually) occupies 1.5MB of heap. It doesn't go away during GC, it doesn't go away when you are short of memory, I even saw these entries in OutOfMemory heapdumps.
The entry is obviously kept by a map java.util.zip.ZipFile$Source.files
. From the source I can tell that this theoretically should be cleaned by a Common-Cleaner thread from InnocuousThreadGroup, but I don't see it happening.
I encountered this problem when migrating a small, lightweight Java app from JDK8 to JDK11. With low Xmx settings those jars use up significant portion of my heap as compared to JDK8.
So is it a bug or a feature?