java.lang.OutOfMemoryError: Metaspace
Environment
- OpenJDK
- Red Hat Build of OpenJDK
- Oracle JDK
Issue
java.lang.OutOfMemoryError: Metaspace- Out of Memory Metaspace errors shortly after startup or certain CLI commands;
Resolution
Either remove or increase -XX:MaxMetaspaceSize:
- Remove
-XX:MaxMetaspaceSizeand let the JVM size the metaspace automatically.
Unlike perm gen, it is typically not necessary to set a max/min Metaspace size for the following reasons:
- Metaspace size is unlimited by default. Since it only holds class metadata, size requirements are minimal.
- It does not require a full gc to resize the metaspace, as it did with the perm generation.
However, if the application is experiencing large metaspace leaks, it may be preferable for the application to fail rather than keep increasing memory size.
- Increase the Metaspace size using the
-XX:MaxMetaspaceSizeJVM argument. For example:
-XX:MaxMetaspaceSize=2g
When UseCompressedOops and UseCompressedClassesPointers are enabled (default), MaxMetaspaceSize includes the CompressedClassSpaceSize.
For example, the following options will result in a 2gb space for class metadata, with 1gb for compressed class pointers:
-XX:MetaspaceSize=2g -XX:MaxMetaspaceSize=2g -XX:CompressedClassSpaceSize=1g
If MaxMetaspaceSize is set smaller than CompressedClassSpaceSize, the JVM auto adjusts CompressedClassSpaceSize as follows:
CompressedClassSpaceSize = MaxMetaspaceSize - 2 * InitialBootClassLoaderMetaspaceSize
For example, the following options will result in a 512M space for class metadata, with 504M (assuming 4M default InitialBootClassLoaderMetaspaceSize) for compressed class pointers:
-XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M -XX:CompressedClassSpaceSize=1024M
See:
- "Metadata GC Threshold" garbage collections are not able to free space.
- JDK 8 Metaspace tuning for JBoss EAP .
- Set
jsp-configdevelopment"false" (or removedevelopment="true", as "false" is the default value):
<jsp-config development="false" tag-pooling="false" check-interval="1" modification-test-interval="1" recompile-on-fail="true"/>
Root Cause
This behavior can happen due to several issues, see the most common bellow:
- The Metaspace is being exhausted. Either it is undersized, or there is a leak.
- The Perm Gen space has been removed in JDK8 and replaced by the Metaspace. Class metadata previously stored in the Perm Gen space is now allocated in the Metaspace in native memory. By default the class metadata allocation is only limited by 32/64 bit addressable memory. The new flag
-XX:MaxMetaspaceSizeargument can be used to limit the amount of native memory used. - Garbage collection of dead classes and classloaders is triggered when class metadata usage reaches
MetaspaceSize. An undersized Metaspace can cause poor performance due to excessive garbage collection. - The application has many classes (e.g. many JSPs).
- There are many static member variables in classes.
- There is heavy use of String.intern().
- There is a classloader leak. If an object outside the classloader holds a reference to an object loaded by the classloader, the classloader cannot be collected.
- There are many isolated classloaders.
- The CMS low pause collector by default does not collect the permanent generation space.
- Hot deployment or redeployment is exposing a classloader leak.
- Hot Deployment Exhausts the PermGen when the Application Uses Ehcache
- Hot Deployment Exhausts the PermGen when the Application Includes Database Drivers
- Spring proxy cache causes PermGen OOME across redeployments
- JBoss application classloaders leak because of unclosed EJBReceiverContexts and EJBClientContexts
- Application with log4j causes perm gen leak across redeployments
- Singleton patterns where a class has a static reference to the class itself, making the class unloadable.
- Heavy use of dynamic proxies.
- JON Agent is failing with error OutOfMemoryError: PermGen space or Java heap space
- AOPClassProxy exhausts PermGen
- Domain controller and host controller lose connection due to 'OutOfMemoryError: PermGen space'
- java.lang.OutOfMemoryError: PermGen when using groovy script in camel
- Perm or Metaspace OOME due to many duplicate JaxbAccessor classes
- Permgen or Metaspace leak at org.apache.cxf.common.util.ProxyClassLoader in JBoss EAP 6.4 CP04/CP05
- Redeploying switchyard app results in OOME
- Perm or Metaspace OOME due to classloaders persisted by ClassLoaderLogContextSelector
- Aborted transactions not being cleared causing memory leak
- Using an EE concurrency executor causes classloader leak from leftover contextClassLoader references
- Application classloader leaked by IIOP WorkCacheManager cache
- Application classloaders leaked by log4j2 jmx beans
- Metaspace filling on EAP 7 with org.jboss.as.security.plugins.ModuleClassLoaderLocator$CombinedClassLoaders
- RestClientExtension manager references causes leak of application classloader in EAP 7
- 'java.lang.OutOfMemoryError: Metaspace' related to ByteBuddy / HibernateProxy classes in JBoss EAP
- Application classloaders leaked by custom JCA provider reference in sun.security.jca.ProviderList
- Application classloaders leaked by com.sun.el.parser.ELParser$LookaheadSuccess
- Failed deployments leak application classloaders in JBoss SuspendController and UndertowContainerProvider
Diagnostic Steps
Java Application Log Analysis
Verify if the OutOfMemoryError (OOME) is due to Metaspace.
GC Log Analysis
Verify a full collection does not reclaim Metaspace.
Classloader Stats Analysis
JDK8 u13+
-
Enable
-XX:+UnlockDiagnosticVMOptionsthen captureGC.class_statswithjcmd(JDK 1.8 u13+):$JAVA_HOME/bin/jcmd <JBOSS_JAVA_PID> GC.class_stats -all -csv > class_stats.csvNotes:
- Forces a full gc.
- Open
class_stats.csvin a spreadsheet (e.g. LibreOffice Calc). - Check
InstBytescolumn for largest retainers. - Review the largest retainers in the heap dump to look for clues. See below.
JDK 1.8 u13-
-
Get
clstatsoutput from a time of high Metaspace piped to a file:$JAVA_HOME/bin/jmap -clstats <JAVA_PID> > clstats.outExample output:
Attaching to process ID 12345, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.101-b13 class_loader classes bytes parent_loader alive? type <bootstrap> 2631 4705272 null live <internal> 0x00000000c91be518 1 1455 0x00000000fdcf0978 dead sun/reflect/DelegatingClassLoader@0x00000001000099d8 0x00000000c377a770 0 0 0x00000000fd618000 live org/eclipse/osgi/internal/loader/EquinoxClassLoader@0x00000001000bf438 0x00000000c64f4a48 454 772114 0x00000000fd618000 live org/eclipse/osgi/internal/loader/EquinoxClassLoader@0x00000001000bf438 ... 0x00000000c7651c90 1 864 0x00000000c35c1d98 dead sun/reflect/DelegatingClassLoader@0x00000001000099d8 0x00000000c2ff5108 54 136855 0x00000000fd618000 live org/eclipse/osgi/internal/loader/EquinoxClassLoader@0x00000001000bf438 0x00000000c76f9898 1 1455 0x00000000c3eb0f60 dead sun/reflect/DelegatingClassLoader@0x00000001000099d8 total = 426 31560 64301535 N/A alive=277, dead=149 N/ANotes:
- Do not use this for JDK 1.8 u13+. It does not seem to provide useful information.
- Requires debuginfo.
- Requires running
jmapas the user that started the Java process. - Will put the JVM in a safepoint (all threads stopped) while running.
- Make take several minutes to produce output.
- Forces a full gc.
Heap Dump Analysis
Get a heap dump when OOME happens using the -XX:+HeapDumpOnOutOfMemoryError option and check the following:
-
Use the following Object Query Language (OQL) query to find duplicate deployments (e.g. duplicate application war/ear/jars):
#EAP6 SELECT module.identifier.name.value.toString() FROM org.jboss.modules.ModuleClassLoader #EAP7 SELECT module.name.value.toString() FROM org.jboss.modules.ModuleClassLoader -
Check for multiple instances of
org.apache.cxf.common.jaxb.JAXBContextCache$2. -
Review the classes that classloader stats analysis showed are the largest retainers for clues. For example, what are the immediate dominators? What are the GC root(s)?
-
Check the stack traces of running threads and review for patterns.
Metaspace OOME Report
JDK8 debug builds with -verbose:gc -XX:+TraceMetadataChunkAllocation and JDK11+ builds with standard recommended gc logging options will result in the following report output at the end of the gc logging that can be used for troubleshooting:
Metaspace (data) allocation failed for size 11
Usage:
Non-class: 218.74 MB capacity, 209.07 MB ( 96%) used, 9.05 MB ( 4%) free+waste, 636.56 KB ( <1%) overhead.
Class: 33.97 MB capacity, 28.95 MB ( 85%) used, 4.71 MB ( 14%) free+waste, 310.88 KB ( <1%) overhead.
Both: 252.71 MB capacity, 238.02 MB ( 94%) used, 13.76 MB ( 5%) free+waste, 947.44 KB ( <1%) overhead.
Virtual space:
Non-class space: 222.00 MB reserved, 221.38 MB (>99%) committed
Class space: 248.00 MB reserved, 34.62 MB ( 14%) committed
Both: 470.00 MB reserved, 256.00 MB ( 54%) committed
Chunk freelists:
Non-Class: 2.62 MB
Class: 672.00 KB
Both: 3.28 MB
MaxMetaspaceSize: 256.00 MB
CompressedClassSpaceSize: 248.00 MB
Initial GC threshold: 96.00 MB
Current GC threshold: 256.00 MB
CDS: on
Check if jsp-config development is set to "true":
<jsp-config development="true" ... />
jcmd VM.metaspace available in JDK11+
Starting with JDK11, even without special java startup options, one can use jcmd <pid> VM.metaspace show-loaders=true show-classes=true command to check the number of loaded classes and Metaspace consumption for each class loader. This allows you to identify the primary factors driving Metaspace usage, such as duplicate or dynamic class loading:
443: CLD 0x0000563e2c6b1cd0: "deployment.app.war" instance of org.jboss.modules.ModuleClassLoader
Loaded classes:
1: com.redhat.gss.example.servlet.ServletA
2: com.redhat.gss.example.servlet.ServletB
3: com.redhat.gss.example.servlet.ServletC
...
-total-: 35 classes
Non-Class: 5 chunks, 80.00 KB capacity, 64.09 KB ( 80%) used, 15.55 KB ( 19%) free, 40 bytes ( <1%) waste, 320 bytes ( <1%) overhead, deallocated: 1 blocks with 24 bytes
Class: 5 chunks, 40.00 KB capacity, 28.84 KB ( 72%) used, 10.84 KB ( 27%) free, 0 bytes ( 0%) waste, 320 bytes ( <1%) overhead, deallocated: 4 blocks with 1.73 KB
Both: 10 chunks, 120.00 KB capacity, 92.94 KB ( 77%) used, 26.40 KB ( 22%) free, 40 bytes ( <1%) waste, 640 bytes ( <1%) overhead, deallocated: 5 blocks with 1.75 KB
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.