The Java heap is used to store object instances. Therefore, if we continuously create objects and ensure that there is an accessible path between GC Root and the created object to avoid the object being garbage collected, then when too many objects are created, it will cause insufficient heap memory, which will raise an OutOfMemoryError exception.
/** * @author xiongyongshun * VM Args: java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError */public class OutOfMemoryErrorTest { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); int i = 0; while (true) { list.add(i++); } }}The above is a code that raises an OutOfMemoryError exception. We can see that it prevents the object from being garbage collected by constantly creating and saving the object in the list. Therefore, when there are too many objects, the heap memory will overflow.
Through java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError, we set the heap memory to 10 megabytes, and use the parameter -XX:+HeapDumpOnOutOfMemoryError to make the JVM print out the current memory snapshot when an OutOfMemoryError exception occurs for subsequent analysis.
After compiling and running the above code, the following output will be:
>>> java -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError com.test.OutOfMemoryErrorTest 16-10-02 23:35java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid1810.hprof ...Heap dump file created [14212861 bytes in 0.125 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3210) at java.util.Arrays.copyOf(Arrays.java:3181) at java.util.ArrayList.grow(ArrayList.java:261) at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235) at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227) at java.util.ArrayList.add(ArrayList.java:458) at com.test.OutOfMemoryErrorTest.main(OutOfMemoryErrorTest.java:15)
Java StackOverflowError
We know that there is a memory area called the virtual machine stack in the runtime data area of the JVM. The function of this area is: each method will create a stack frame when it is executed, which is used to store information such as local variable tables, operand stacks, method exits, etc.
Therefore, we can create an infinitely recursive recursive call. When the recursive depth is too large, the stack space will be exhausted, which will lead to a StackOverflowError exception.
Here is the specific code:
/** * @author xiongyongshun * VM Args: java -Xss64k */public class OutOfMemoryErrorTest { public static void main(String[] args) { stackOutOfMemoryError(1); } public static void stackOutOfMemoryError(int depth) { depth++; stackOutOfMemoryError(depth); }}After compiling and running the above code, the following exception information will be output:
Exception in thread "main" java.lang.StackOverflowError at com.test.OutOfMemoryErrorTest.stackOutOfMemoryError(OutOfMemoryErrorTest.java:27)
Memory overflow in method area
Note that because JDK8 has removed the permanent generation and replaced it with metaspace, in JDK8, neither of the following two examples will cause java.lang.OutOfMemoryError: PermGen space exception.
Constant pool overflows during runtime
In Java 1.6 and previous versions of HotSpot JVM, there is a concept of permanent generation, that is, the generation collection mechanism of GC is extended to the method area. In the method area, a part of the memory is used to store constant pools. Therefore, if there are too many constants in the code, the memory of the constant pool will be exhausted, resulting in memory overflow. So how to add a large number of constants to the constant pool? At this time, you need to rely on the String.intern() method. The function of the String.intern() method is: If the value of this String already exists in the constant pool, this method returns the reference to the corresponding string in the constant pool; otherwise, add the value contained in this String to the constant pool and return the reference to this String object. In JDK 1.6 and previous versions, the constant pool is allocated in the permanent generation. Therefore, we can indirectly limit the size of the constant pool by setting the parameters "-XX:PermSize" and "-XX:MaxPermSize".
Note that the memory distribution of the String.intern() method mentioned above and the constant pool is only for JDK 1.6 and previous versions. In JDK 1.7 or above, the memory layout is slightly different because the concept of permanent generation is removed.
The following is an example of code to implement memory overflow of constant pools:
/** * @author xiongyongshun * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M */public class RuntimeConstantPoolOOMTest { public static void main(String[] args) { List<String> list = new ArrayList<String>(); int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } }}We see that in this example, it is precisely using the String.intern() method to add a large number of string constants to the constant pool, which leads to memory overflow of the constant pool.
We compile and run the above code through JDK1.6, and the following output will be:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at com.test.RuntimeConstantPoolOOMTest.main(RuntimeConstantPoolOOMTest.java:16)
It should be noted that if the above code is compiled and run through JDK1.8, there will be the following warning and no exception will be generated:
>>> java -XX:PermSize=10M -XX:MaxPermSize=10M com.test.RuntimeConstantPoolOOMTest 16-10-03 0:23Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10M; support was removed in 8.0 Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10M; support was removed in 8.0
Memory overflow in the method area
The function of the method area is to store the relevant information of Class, such as class names, class access modifiers, field descriptions, method descriptions, etc. Therefore, if the method area is too small and too many classes are loaded, the memory overflow of the method area.
//VM Args: -XX:PermSize=10M -XX:MaxPermSize=10Mpublic class MethodAreaOOMTest { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MethodAreaOOMTest.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); } }); enhancer.create(); } }}In the above code, we use CGlib to dynamically generate a large number of classes. In JDK6, running the above code will generate an OutOfMemoryError: PermGen space exception:
/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java -jar -XX:PermSize=10M -XX:MaxPermSize=10M target/Test-1.0-SNAPSHOT.jar
The output result is as follows:
Caused by: java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:637) at java.lang.ClassLoader.defineClass(ClassLoader.java:621) ... 11 more
MetaSpace Memory Overflow
In the Memory Overflow Memory Overflow Section in the Method Area, we mentioned that JDK8 has no concept of permanent generation, so those two examples did not achieve the expected effect under JDK8. So in JDK8, are there any errors like Memory Overflow in the Method Area? Of course, some. In JDK8, MetaSpace area is used to store Class related information, so when MetaSpace is insufficient, a java.lang.OutOfMemoryError: Metaspace exception will be thrown.
Let’s take the example mentioned above as an example:
//VM Args: -XX:MaxMetaspaceSize=10Mpublic class MethodAreaOOMTest { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MethodAreaOOMTest.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return methodProxy.invokeSuper(o, objects); } }); enhancer.create(); } }}The code part of this example has not been changed. The only difference is that we need to use JDK8 to run this code, and set the parameter -XX:MaxMetaspaceSize=10M. This parameter tells the JVM that the maximum size of Metaspace is 10M.
Then we use JDK8 to compile and run this example, and output the following exception:
>>> java -jar -XX:MaxMetaspaceSize=10M target/Test-1.0-SNAPSHOT.jarException in thread "main" java.lang.OutOfMemoryError: Metaspace at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345) at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492) at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305) at com.test.MethodAreaOOMTest.main(MethodAreaOOMTest.java:22)
Summarize
The above is all about common memory overflow exceptions and code examples in this article, I hope it will be helpful to everyone. Interested friends can continue to refer to other related topics on this site. If there are any shortcomings, please leave a message to point it out. Thank you friends for your support for this site!