There is a “high wall” between Java and C++ surrounded by dynamic memory allocation and garbage collection techniques. Those on the outside want to get in, but those on the inside want to get out. Understanding the Java Virtual Machine: Advanced JVM Features and Best Time Practices (2nd edition) by Zhiming Zhou

As a computer abstracted from running Java programs, the Java virtual machine has the ability of memory management, such as memory allocation, garbage collection and other related memory management problems, so the Java programmer is happier than the C++ programmer, but once there is a problem in memory, If you don’t know how your virtual machine is using memory, it can be difficult to troubleshoot errors.

This article is the third in a series of in-depth understanding of the Java Virtual Machine: Advanced JVM Features and Best Time Practices (2nd edition) by Zhiming Chou.

3. The memory is abnormal

Although there are Java virtual machines to help us manage memory, there are still memory exceptions during the management process. In addition to the program counters mentioned earlier in memory partitioning, all regions are subject to OutOfMemoryError exceptions.

We can set parameters to the Java virtual machine to simulate the occurrence of these exceptions. Different Java virtual machines may run differently, using the Oracle JDK.

Note: Unless otherwise specified, JDK8 is used by default.

3.1 The Java heap memory is abnormal

The Java heap is used to store object instances, so you can simulate anomalies in the Java heap memory by constantly creating objects to fill the Java heap area and keeping in mind that garbage collection can’t clean up these objects.

The simulation program code is as follows:

import java.util.ArrayList;
import java.util.List;

// Simulate a Java heap memory exception
public class HeapOOM {
	// Declare an internal static class with a lifetime as long as the external HeapOOM class, so that the garbage collector cannot reclaim the memory space occupied by these objects
	static class OOMObject{}public static void main(String[] args) {
		List<OOMObject> list = new ArrayList<>();
		// An infinite loop keeps generating objects and adding them to the list until the Java heap is full
		while(true) {
			list.add(newOOMObject()); }}}Copy the code

Here use the MAT Memory Analyzer plug-in to analyze Memory exceptions, IDE use free Eclipse, of course, IDEA can also be installed, Eclipse installation tutorial can see this article “ONE of the MAT – Eclipse install Memory Analyzer”.

On the Debug configuration page, set the JVM parameters.

JVM Debug parameter:

-verbose:gc -Xms20M -Xmx20M

-XX:+PrintGCDetails

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=D:\CodeWorkspace\Java\Dump

-xms, -xmx, -xmn, -xmn, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx, -xx HeapDumpOnOutOfMemoryError occurs when an OutOfMemoryError record memory snapshot, HeapDumpPath followed by storage memory snapshots folder location.

The debugging result is as follows:

From the figure above, you can see that the Java Heap space has an OutOfMemoryError exception and a memory snapshot file has been generated in the folder we specified. Before using THE MAT memory analyzer tool, we also need to know the difference between memory leak and memory overflow. I did not translate OutOfMemoryError into memory leak or memory overflow exception, but used the original English, memory leak and memory overflow are only the cause of the exception. The result of this event is an OutOfMemoryError exception.

The difference between a memory leak and a memory overflow:

  • Memory leakage means that an application fails to release the allocated memory space after applying for it. As a result, memory resources are used up. Generally speaking, an object occupies the memory space and does not return it to the system.
  • Memory overflow refers to the fact that when a program applies for memory, it finds that the memory space is insufficient. A common example is that when a large number is saved, the maximum value of the data type is exceeded. Generally speaking, the program finds that the memory space cannot meet its own requirements.

Knowing the difference between memory leak and memory overflow, let’s use MAT to analyze memory snapshot. First, bring up MAT view, and then select “Open Heap Dump” from the “File” option to Open memory snapshot File.

After opening the snapshot file, you can clearly see the Problem suspects where memory exceptions are likely to occur.

Click on Details to see the Details.

If the OOMObject has a reference chain to GC Roots, the garbage collector cannot reclaim the memory occupied by the object. Therefore, OutOfMemoryError is caused by a memory leak.

3.2 The stack memory is abnormal

There is no distinction between the Java virtual machine stack and the local method stack in the HotSpot VIRTUAL machine, and the stack size can be set with the -xSS parameter.

Two types of stack exceptions are described in the Java Virtual Machine specification:

  • Throw StackOverflowError if the stack depth requested by the thread is greater than the virtual machine allows.
  • OutOfMemoryError is thrown if the virtual stack cannot allocate enough memory during dynamic expansion.

The depth of the stack is determined by the memory space of the stack. The deeper the requested stack is, the greater the used stack space. Therefore, the two exceptions in the Java Virtual Machine specification above overlap, and one exception may cause another. Is the memory exception caused by the small memory space of the stack or the memory exception caused by the large memory space of the used stack?

Reducing stack memory size and defining a large number of local variables to increase the length of the local variable table in the stack frame are both theoretically possible StackOverflowError and OutOfMemoryError exceptions.

But the following code can only raise StackOverflowError exceptions.

// StackOverflowError is abnormal
public class JVMStackSOF {
	private int stackLength = 1;
	// a recursive function
	public void stackLeak(a) {
		stackLength++;
		stackLeak();
	}
	public static void main(String[] args) {
		JVMStackSOF stackSOF = new JVMStackSOF();
		try {
			stackSOF.stackLeak();
		} catch (Throwable e) {
			System.out.println("Stack Length:" + stackSOF.stackLength);
			throwe; }}}Copy the code

The Debug parameters are as follows: -verbose:gc -Xss128k -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\CodeWorkspace\Java\Dump

The Debug result is as follows, and only StackOverflowError is generated.

In a multi-threaded environment, OutOfMemoryError can be simulated.

Special reminder: this code will cause the system to suspend operation, with a certain risk, please save other files before running.

The code is as follows:

// Stack OutOfMemoryError is abnormal
public class JVMStackOOM {
	private void dontStop(a) {
		while(true) {}}/ /! Do not try dangerous code
	public void stackLeakByTread(a) {
		// An endless loop keeps creating threads
		while(true) {
			Thread thread = new Thread(new Runnable() {
				
				@Override
				public void run(a) { dontStop(); }}); thread.start(); }}public static void main(String[] args) {
		JVMStackOOM stackOOM = newJVMStackOOM(); stackOOM.stackLeakByTread(); }}Copy the code

Since the system died while doing this dangerous test, I do not have actual results, according to the in-depth Understanding of the Java Virtual Machine: Advanced JVM Features and Best Time Practices (2nd edition), which gives theoretical results. You can also try running this code on a virtual machine system, but the external system may also appear to be dead, so you can try for yourself.

3.3 Method area memory is abnormal

The method area has a run-time constant pool. Adding a large amount of content to the constant pool can also cause memory exceptions in the method area. You can limit the size of the method area by using -xx :Permsize and -xx :MaxPermSize to limit the capacity of the constant pool. Constants can be added to the constant pool at compile time, and new constants can be added at run time. No memory is occupied and cannot be reclaimed, so the exception here is not a memory leak, but a memory overflow.

The code is as follows:

import java.util.ArrayList;
import java.util.List;

// The constant pool in the simulation method area is out of memory
public class RuntimeConstantPoolOOM {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		int i = 0;
		while(true) { list.add(String.valueOf(i++).intern()); }}}Copy the code

In JDK7 and JDK8, we found that the garbage collector constantly reclaims the memory of old constants in the constant pool so that new constants can enter, thus avoiding constant pool exceptions.

The method area is used to store information about the class, such as class name, access modifiers, constant pool, field description, method description, and so on. The general idea behind the method area memory exception is to create a large number of classes that fill the method area until the method area memory runs out. Since the experiment is cumbersome to operate, directly manipulating bytecode files to generate a large number of classes dynamically, the results from the book are also used here.

3.4 Direct Memory Is Abnormal

The size of the immediate memory can be specified using -xx :MaxDirectMemorySize. If not, the default is the same as the maximum value of the Java heap (-xmx). Using the Unsafe class, the Unsafe class is restricted, and only the loader of the boot class returns object instances. Therefore, the Unsafe class instance can only be retrieved through reflection, but importing the package in Eclipse will result in an error. See The Resources article for a solution.

Reference article:

Addressing import sun.misc.Unsafe in Eclipse

The code is as follows:

import java.lang.reflect.Field;
import sun.misc.Unsafe;

// Simulate a direct memory exception
public class DirectMemoryOOM {
	private static final int _1MB = 1024 * 1024;
	public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
		Field unsafeField = Unsafe.class.getDeclaredFields()[0];
		unsafeField.setAccessible(true);
		Unsafe unsafe = (Unsafe)unsafeField.get(null);
		while(true) {
			unsafe.allocateMemory(_1MB); // Request memory}}}Copy the code

Debug parameters: -verbose:gc -XMx20m-xx :MaxDirectMemorySize= 10m-xx :+PrintGCDetails

Because in Eclipse using JDK6 and JDK7 run the program will directly flash back, can not get the output exception, so directly in the console using JDK8 compile and run the program, the running result is as follows:

Summary: Emulating memory exceptions is a dangerous business, so it’s important to save your various files before testing to avoid losing their contents.

Think the article is good, you can pay attention to the programming heart wechat public number, in the way of programming, we grow up together.