I have a task to simulate OutOfMemory: Metaspace error intentionally. I tried different ways, but none gave me needed result. Can someone share good example for this purpose?
My first attempt (using javassist):
static ClassPool classPool = ClassPool.getDefault(); @SneakyThrows public void task() { try { for (int i = 0; ; i++) { Class cl = classPool.makeClass("task1.Test" + i).toClass(); } } catch (Error er) { Runtime.getRuntime().gc(); log.error(er.getMessage()); } }
and settings in gradle.properties file:
org.gradle.jvmargs=-XX:MaxMetaspaceSize=70M
but I’ve got an error:
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread “main”
which I couldn’t catch.
My second attempt:
public void task() { try { URL url = new File("D:/classes").toURI().toURL(); URL[] urls = {url}; ClassLoadingMXBean loadingMXBean = ManagementFactory.getClassLoadingMXBean(); List<ClassLoader> classLoaders = new ArrayList<>(); while (true) { ClassLoader classLoader = new URLClassLoader(urls); classLoaders.add(classLoader); classLoader.loadClass("test1.Test1"); log.info("Total: " + loadingMXBean.getTotalLoadedClassCount()); log.info("Active: " + loadingMXBean.getLoadedClassCount()); log.info("Unloaded: " + loadingMXBean.getUnloadedClassCount()); } } catch (Exception ex) { log.error(ex + ex.getMessage()); } }
and it didn’t work, the IDEA notified me there’s law memory.
Advertisement
Answer
You are forgetting how classloading works in the JVM. A classloader will first try to let its parent load the class you are looking for. In order for your test to work, you need a custom class loader that will not delegate to its parent for that one class that you are testing with.
Here is an example that works in Java 8
DummyClass.java (default package)
public class DummyClass { static String padding = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; }
Test.java (default package)
import java.lang.management.ClassLoadingMXBean; import java.lang.management.ManagementFactory; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { try { URL url = Test.class.getResource("").toURI().toURL(); System.out.println("URL = " + url); URL[] urls = {url}; ClassLoadingMXBean loadingMXBean = ManagementFactory.getClassLoadingMXBean(); List<ClassLoader> classLoaders = new ArrayList<>(); System.out.println("Total: " + loadingMXBean.getTotalLoadedClassCount()); System.out.println("Active: " + loadingMXBean.getLoadedClassCount()); System.out.println("Unloaded: " + loadingMXBean.getUnloadedClassCount()); int i = 0; while ( true ) { i++; System.out.println("### Iteration " + i + " ###"); ClassLoader classLoader = new URLClassLoader(urls) { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { final Class<?> loadedClass; if ( "DummyClass".equals(name) ) { System.out.println (this + " - finding class " + name); loadedClass = findClass(name); System.out.println (this + " - loading class " + loadedClass); } else { // delegate to parent loadedClass = this.getParent().loadClass(name); } return loadedClass; } }; classLoaders.add(classLoader); classLoader.loadClass("DummyClass"); System.out.println("Total: " + loadingMXBean.getTotalLoadedClassCount()); System.out.println("Active: " + loadingMXBean.getLoadedClassCount()); System.out.println("Unloaded: " + loadingMXBean.getUnloadedClassCount()); } } catch ( Exception ex ) { ex.printStackTrace(); } } }
Running with -XX:MaxMetaspaceSize=100m
, I eventually get this:
> ### Iteration 16318 ### > Test$1@531ed68e - finding class DummyClass > Test$1@531ed68e - loading class class DummyClass > Total: 16747 Active: > 16747 Unloaded: 0 > ### Iteration 16319 ### > Test$1@6bbd4048 - finding class DummyClass > Exception in thread "main" java.lang.OutOfMemoryError: Metaspace at > java.lang.ClassLoader.defineClass1(Native Method) at > java.lang.ClassLoader.defineClass(ClassLoader.java:756) at > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) > at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) at > java.net.URLClassLoader.access$100(URLClassLoader.java:74) at > java.net.URLClassLoader$1.run(URLClassLoader.java:369) at > java.net.URLClassLoader$1.run(URLClassLoader.java:363) at > java.security.AccessController.doPrivileged(Native Method) at > java.net.URLClassLoader.findClass(URLClassLoader.java:362) at > Test$1.loadClass(Test.java:42) at Test.main(Test.java:53)