查看JVM参数的网站地址: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
Java堆内存溢出异常测试
package jvm;
import java.util.ArrayList;
import java.util.List;
/**
* @author: guanglai.zhou
* @date: 2021/6/17 13:27
* @description: VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
* @version: 1.0
*/
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) {
list.add(new OOMObject());
}
}
}
设置JVM参数 -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
执行结果堆内存溢出,并且生成dump文件
控制台信息如下
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3184.hprof ...
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:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at jvm.HeapOOM.main(HeapOOM.java:23)
Heap dump file created [28453527 bytes in 0.185 secs]
通过工具分析dump文件,在jdk的工具中打开jvisualvm.exe
,通过文件->装入
加载堆dump文件
基本信息如下所示
生成的日期: Thu Jun 17 13:30:48 CST 2021
文件: E:\spring\docker-demo\java_pid3184.hprof
文件大小: 27.1 MB
字节总数: 20,361,853
类总数: 606
实例总数: 819,846
类加载器: 2
垃圾回收根节点: 593
等待结束的暂挂对象数: 0
在出现 OutOfMemoryError 异常错误时进行了堆转储
导致 OutOfMemoryError 异常错误的线程: main
还可以看到系统属性
awt.toolkit=sun.awt.windows.WToolkit
file.encoding=UTF-8
file.encoding.pkg=sun.io
file.separator=\
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.awt.printerjob=sun.awt.windows.WPrinterJob
java.class.path=C:\Program Files\Java\jdk1.8.0_202\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar;E:\spring\docker-demo\target\classes;D:\maven\repo\org\springframework\boot\spring-boot-starter-web\2.4.4\spring-boot-starter-web-2.4.4.jar;D:\maven\repo\org\springframework\boot\spring-boot-starter\2.4.4\spring-boot-starter-2.4.4.jar;D:\maven\repo\org\springframework\boot\spring-boot\2.4.4\spring-boot-2.4.4.jar;D:\maven\repo\org\springframework\boot\spring-boot-autoconfigure\2.4.4\spring-boot-autoconfigure-2.4.4.jar;D:\maven\repo\org\springframework\boot\spring-boot-starter-logging\2.4.4\spring-boot-starter-logging-2.4.4.jar;D:\maven\repo\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\maven\repo\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\maven\repo\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;D:\maven\repo\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;D:\maven\repo\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\maven\repo\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\maven\repo\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;D:\maven\repo\org\springframework\boot\spring-boot-starter-json\2.4.4\spring-boot-starter-json-2.4.4.jar;D:\maven\repo\com\fasterxml\jackson\core\jackson-databind\2.11.4\jackson-databind-2.11.4.jar;D:\maven\repo\com\fasterxml\jackson\core\jackson-annotations\2.11.4\jackson-annotations-2.11.4.jar;D:\maven\repo\com\fasterxml\jackson\core\jackson-core\2.11.4\jackson-core-2.11.4.jar;D:\maven\repo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.4\jackson-datatype-jdk8-2.11.4.jar;D:\maven\repo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.4\jackson-datatype-jsr310-2.11.4.jar;D:\maven\repo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.4\jackson-module-parameter-names-2.11.4.jar;D:\maven\repo\org\springframework\boot\spring-boot-starter-tomcat\2.4.4\spring-boot-starter-tomcat-2.4.4.jar;D:\maven\repo\org\apache\tomcat\embed\tomcat-embed-core\9.0.44\tomcat-embed-core-9.0.44.jar;D:\maven\repo\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;D:\maven\repo\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.44\tomcat-embed-websocket-9.0.44.jar;D:\maven\repo\org\springframework\spring-web\5.3.5\spring-web-5.3.5.jar;D:\maven\repo\org\springframework\spring-beans\5.3.5\spring-beans-5.3.5.jar;D:\maven\repo\org\springframework\spring-webmvc\5.3.5\spring-webmvc-5.3.5.jar;D:\maven\repo\org\springframework\spring-aop\5.3.5\spring-aop-5.3.5.jar;D:\maven\repo\org\springframework\spring-context\5.3.5\spring-context-5.3.5.jar;D:\maven\repo\org\springframework\spring-expression\5.3.5\spring-expression-5.3.5.jar;D:\maven\repo\commons-io\commons-io\2.6\commons-io-2.6.jar;D:\maven\repo\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\maven\repo\org\springframework\spring-core\5.3.5\spring-core-5.3.5.jar;D:\maven\repo\org\springframework\spring-jcl\5.3.5\spring-jcl-5.3.5.jar;E:\Program Files\JetBrains\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar
java.class.version=52.0
java.endorsed.dirs=C:\Program Files\Java\jdk1.8.0_202\jre\lib\endorsed
java.ext.dirs=C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
java.home=C:\Program Files\Java\jdk1.8.0_202\jre
java.io.tmpdir=C:\Users\GUANGL~1.ZHO\AppData\Local\Temp\
java.library.path=C:\Program Files\Java\jdk1.8.0_202\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Program Files\Java\jdk1.8.0_202\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Lenovo\Fingerprint Manager Pro\;D:\Program Files\apache-maven-3.6.3\bin;C:\Program Files\TortoiseSVN\bin;E:\spring\spring-boot-cli-2.2.0.RELEASE-bin\spring-2.2.0.RELEASE\bin;D:\Program Files\Git\cmd;D:\Program Files\gradle-7.0\bin;.
java.runtime.name=Java(TM) SE Runtime Environment
java.runtime.version=1.8.0_202-b08
java.specification.name=Java Platform API Specification
java.specification.vendor=Oracle Corporation
java.specification.version=1.8
java.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
java.vendor.url.bug=http://bugreport.sun.com/bugreport/
java.version=1.8.0_202
java.vm.info=mixed mode
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
java.vm.specification.name=Java Virtual Machine Specification
java.vm.specification.vendor=Oracle Corporation
java.vm.specification.version=1.8
java.vm.vendor=Oracle Corporation
java.vm.version=25.202-b08
line.separator=\r\n
os.arch=amd64
os.name=Windows 7
os.version=6.1
path.separator=;
sun.arch.data.model=64
sun.boot.class.path=C:\Program Files\Java\jdk1.8.0_202\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_202\jre\classes
sun.boot.library.path=C:\Program Files\Java\jdk1.8.0_202\jre\bin
sun.cpu.endian=little
sun.cpu.isalist=amd64
sun.desktop=windows
sun.io.unicode.encoding=UnicodeLittle
sun.java.command=jvm.HeapOOM
sun.java.launcher=SUN_STANDARD
sun.jnu.encoding=GBK
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
sun.os.patch.level=Service Pack 1
user.country=CN
user.dir=E:\spring\docker-demo
user.home=C:\Users\guanglai.zhou
user.language=zh
user.name=guanglai.zhou
user.script=
user.timezone=
user.variant=
在类选项中可以看到98.8的实例为OOMObject类型
运行时常量池导致的内存溢出异常
package jvm;
import java.util.HashSet;
import java.util.Set;
/**
* @author: guanglai.zhou
* @date: 2021/6/17 13:45
* @description: VM args: -XX:PermSize=6M -XX:MaxPermSize=6M
* 在JDK7以上 PermSize已经无效了
* 需要设置堆的大小 -Xmx6m
* @version: 1.0
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
short i = 0;
while (true){
set.add(String.valueOf(i++).intern());
}
}
}
运行结果如下
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3099)
at jvm.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:20)
以上结果是在JDK8中的测试结果(单纯设置JVM参数-XX:PermSize=6M -XX:MaxPermSize=6M
是没有作用,程序会一直跑下去),因为在JDK7之后字符串常量是存放在堆当中的,所以设置永久代大小已经没有任何作用了,只能通过限制堆的大小测试出异常。
方法区内存溢出异常
在JDK1.8当中,不存在永久代,方法区其实是在元空间中的,因为通过指定元空间的大小来测试方法区内存溢出异常
设置元空间最大值
-XX:MaxMetaspaceSize=size
Sets the maximum amount of native memory that can be allocated for class metadata. By default, the size is not limited. The amount of metadata for an application depends on the application itself, other running applications, and the amount of memory available on the system.
The following example shows how to set the maximum class metadata size to 256 MB:
-XX:MaxMetaspaceSize=256m
设置元空间开始大小
-XX:MetaspaceSize=size
Sets the size of the allocated class metadata space that will trigger a garbage collection the first time it is exceeded. This threshold for a garbage collection is increased or decreased depending on the amount of metadata used. The default size depends on the platform.
package jvm;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author: guanglai.zhou
* @date: 2021/6/17 13:59
* @description: VM args: -XX:PermSize=6M -XX:MaxPermSize=6M
* 在JDK1.8当中 设置永久代没有意义了 方法区放在了元空间 通过以下命令设置元空间大小
* -XX:MaxMetaspaceSize=64m -XX:MetaspaceSize=64m
* @version: 1.0
*/
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o, args);
}
});
enhancer.create();
}
}
static class OOMObject {
}
}
运行结果如下
Exception in thread "main" org.springframework.cglib.core.CodeGenerationException: java.lang.OutOfMemoryError-->Metaspace
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:557)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363)
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:131)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572)
at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:387)
at jvm.JavaMethodAreaOOM.main(JavaMethodAreaOOM.java:30)
Caused by: java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:554)
... 7 more
Process finished with exit code 1
直接内存溢出异常
package jvm;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @author: guanglai.zhou
* @date: 2021/6/17 14:36
* @description: -Xmx20m -XX:MaxDirectMemorySize=10M
* @version: 1.0
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
设置直接内存大小
-XX:MaxDirectMemorySize=size
Sets the maximum total size (in bytes) of the New I/O (the java.nio package) direct-buffer allocations. Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, g or G to indicate gigabytes. By default, the size is set to 0, meaning that the JVM chooses the size for NIO direct-buffer allocations automatically.
The following examples illustrate how to set the NIO size to 1024 KB in different units:
-XX:MaxDirectMemorySize=1m
-XX:MaxDirectMemorySize=1024k
-XX:MaxDirectMemorySize=1048576