The JDK 1.8 early access is available for download at Oracle’s site. In this short tutorial we will try to use it to start up Wildfly 8 application server.
So once downloaded, installed and configured JAVA_HOME to point to the new JDK 8, let’s now start wildfly 8. The first change that we notice is that there is a warning issued because the PermGen space configuration parameter is not available anymore.
“Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0”
What happened in JDK 1.8 is that the Permanent generation space has been deprecated and now class metadata information is contained into a JVM native space called Metaspace.
In order to configure the metaspace a new option is available called MaxMetaspaceSize: this flag allows you to trigger collection of dead classes once the MaxMetaspaceSize is reached. If you don’t specify this option the Metaspace will dynamically re-size depending of the application demand at runtime. This will eventually lead to memory exaustion if you have a memory leak in your application.
Got the theory, let’s change the standalone.conf.bat/standalone.conf file to include the new flag. (The same information can be included in your domain.xml/host.xml file depending if you want to vary the server groups jvm settings or the host/server jvm settings)
set "JAVA_OPTS=-Xms64M -Xmx512M -XX:MaxMetaspaceSize=256M -Xloggc:C:\wildfly-8.0.0.Final\standalone\log\jvm.log -verbose:gc -XX:+PrintGCDetails"
Additionally we have included a verbose GC logging so that we can check how much memory the application server consumes for storing class metadata.
Next, I’ve run a CLI script which deploys and undeploys an application in a loop 1000 times. From the verbose garbage collector logs I’ve collected evidence of the following full garbage collections:
1.981: [Full GC (Metadata GC Threshold) [PSYoungGen: 2988K->0K(37376K)] [ParOldGen: 11107K->9101K(64000K)] 14096K->9101K(101376K), [Metaspace: 19076K->19076K(1067008K)], 0.0611726 secs] [Times: user=0.16 sys=0.00, real=0.06 secs]
3.289: [Full GC (Metadata GC Threshold) [PSYoungGen: 5098K->0K(50176K)] [ParOldGen: 17283K->17555K(64000K)] 22382K->17555K(114176K), [Metaspace: 31221K->31221K(1079296K)], 0.0883060 secs] [Times: user=0.25 sys=0.00, real=0.09 secs]
5.423: [Full GC (Metadata GC Threshold) [PSYoungGen: 9298K->0K(71168K)] [ParOldGen: 26505K->29941K(64000K)] 35803K->29941K(135168K), [Metaspace: 52141K->52141K(1099776K)], 0.3500207 secs] [Times: user=1.14 sys=0.00, real=0.35 secs]
In the end, the Metaspace committed about 87MB of space in the JVM and about 10MB of class space:
PSYoungGen total 134144K, used 50594K [0x00000000f5580000, 0x00000000ffe00000, 0x0000000100000000)
eden space 95744K, 24% used [0x00000000f5580000,0x00000000f6ca8bc0,0x00000000fb300000)
from space 38400K, 70% used [0x00000000fd880000,0x00000000ff2c0000,0x00000000ffe00000)
to space 38400K, 0% used [0x00000000fb300000,0x00000000fb300000,0x00000000fd880000)
ParOldGen total 64000K, used 59067K [0x00000000e0000000, 0x00000000e3e80000, 0x00000000f5580000)
object space 64000K, 92% used [0x00000000e0000000,0x00000000e39aed98,0x00000000e3e80000)
Metaspace used 78988K, capacity 86940K, committed 87140K, reserved 1124352K
class space used 10340K, capacity 12847K, committed 12900K, reserved 1048576K
Now I have executed the same script but using JVM 1.7 and the older PermGen:
2.915: [Full GC [PSYoungGen: 2544K->0K(70144K)] [ParOldGen: 31511K->25184K(69120K)] 34055K->25184K(139264K) [PSPermGen: 32478K->32117K(65024K)], 0.1899562 secs] [Times: user=0.39 sys=0.00, real=0.19 secs]
156.543: [Full GC [PSYoungGen: 27024K->0K(117248K)] [ParOldGen: 57563K->55795K(112128K)] 84587K->55795K(229376K) [PSPermGen: 78865K->77621K(128000K)], 0.6084730 secs] [Times: user=1.47 sys=0.00, real=0.61 secs]
As you can see, full garbage collections are less frequent, yet they are more expensive in terms of time needed to complete, ending up with the following heap space:
PSYoungGen total 124928K, used 46968K [0x00000000f5500000, 0x00000000ff300000, 0x0000000100000000)
eden space 88064K, 30% used [0x00000000f5500000,0x00000000f6f96338,0x00000000fab00000)
from space 36864K, 53% used [0x00000000fcf00000,0x00000000fe248010,0x00000000ff300000)
to space 36864K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fcf00000)
ParOldGen total 112128K, used 55795K [0x00000000e0000000, 0x00000000e6d80000, 0x00000000f5500000)
object space 112128K, 49% used [0x00000000e0000000,0x00000000e367ce30,0x00000000e6d80000)
PSPermGen total 128000K, used 78298K [0x00000000d0000000, 0x00000000d7d00000, 0x00000000e0000000)
object space 128000K, 61% used [0x00000000d0000000,0x00000000d4c76800,0x00000000d7d00000)
The PermGen object space (that should be equivalent to the class space of the Metaspace) used up to 61% of the available spac. So it seems that the new Metaspace collection is more frequent yet more efficient than the older PerGen counterpart. Anyway, even if the MaxMetaspaceSize is set, should any classloader leak exist, the JVM seems sensible to set a similar limit to what was set with MaxPermSize. This means that the metaspace can still run out of memory, causing a “java.lang.OutOfMemoryError: Metadata space”.