内存溢出(OOM)
常见场景
- 元数据空间
- 线程私有的Java虚拟机栈
- 堆内存空间
Metaspace内存溢出
什么情况下会发生Metaspace内存溢出呢?
-
元数据空间参数设置过小 ,512mb一般足够了
-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -
使用cglib之类的技术动态生成一些类,一旦代码中没有控制好,导致你生成的类过于多的时候,就很容易把Metaspace给塞满,进而引发内存溢出
模拟场景
-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
if (method.getName().equals("run")) {
System.out.println("before run,do safety check");
return methodProxy.invokeSuper(o, objects);
}
return methodProxy.invokeSuper(o, objects);
}
});
Car car = (Car)enhancer.create();
car.run();
}
}
static class Car {
public void run() {
System.out.println("car is running");
}
}
Caused by: java.lang.OutOfMemoryError: Metaspace
Java虚拟机栈内存溢出
无限制递归调用
模拟场景
-XX:ThreadStackSize=1m
private static long count = 0;
public static void main(String[] args) {
work();
}
public static void work() {
System.out.println("目前是第" + (++count) + "次方法调用");
work();
}
Exception in thread "main" java.lang.StackOverflowError
堆内存溢出
发生堆内存溢出的原因:
有限的内存中放了过多的对象,而且大多数都是存活的,此时即使GC过后还是大部分都存活,所以要继续放入更多对象已经不可能了,此时只能引发内存溢出问题。
一般来说发生内存溢出有两种主要的场景:
-
系统承载高并发请求,因为请求量过大,导致大量对象都是存活的,所以要继续放入新的对象实在是不行了,此时就会引发OOM系统崩渍
-
系统有内存泄漏的问题,就是莫名其妙弄了很多的对象,结果对象都是存活的,没有及时取消对他们的引用,导致触发GC还是无法回收,此时只能引发内存溢出,因为内存实在放不下更多对象了
因此总结起来,一般引发OOM,要不然是系统负载过高,要不然就是有内存泄漏的问题
实战
在JVM内存溢出的时候自动dump内存快照
JVM本身在发生OOM之前都会尽可能的去进行GC腾出来一些内存空间。
OOM的发生并不是大家想的那样,突然之间内存太多了,JVM自己都没反应过来就直接崩溃了,并非如此。
JVM启动参数设置
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/app/oom
JVM参数模板:
-Xms4096M-Xmx4096M-Xmn3072M -Xss1M -XX:MetaspaceSize=256M-XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFaction=92
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSParallellnitialMarkEnabled -XX:+CMSScavengeBeforeRemark
-XX:+DisableExplicitGC -XX:+PrintGCDetails -Xloggc:gc.log
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/app/oom
Metaspace区域内存溢出的时候,应该如何解决
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Car.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
if (method.getName().equals("run")) {
System.out.println("before run,do safety check");
return methodProxy.invokeSuper(o, objects);
}
return methodProxy.invokeSuper(o, objects);
}
});
Car car = (Car)enhancer.create();
car.run();
}
}
static class Car {
public void run() {
System.out.println("car is running");
}
}
JVM启动参数设置:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
-XX:+PrintGCDetails -Xloggc:gc.log
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
./ 表示在当前项目的根目录下。
此时可以找到java_pid21896.hprof文件。使用MAT分析文件可知,我们利用cglib创建了太多类。
gc日志:
OpenJDK 64-Bit Server VM (25.252-b09) for windows-amd64 JRE (1.8.0_252-b09), built on Apr 22 2020 09:32:45 by "jenkins" with MS VC++ 12.0 (VS2013)
Memory: 4k page, physical 8045256k(1306212k free), swap 20823552k(4196040k free)
CommandLine flags: -XX:CompressedClassSpaceSize=2097152 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ -XX:InitialHeapSize=128724096 -XX:MaxHeapSize=2059585536 -XX:MaxMetaspaceSize=10485760 -XX:MaxNewSize=686469120 -XX:MaxTenuringThreshold=6 -XX:MetaspaceSize=10485760 -XX:OldPLABSize=16 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC
0.551: [GC (Allocation Failure) 0.551: [ParNew: 33856K->2179K(38080K), 0.0025936 secs] 33856K->2179K(122752K), 0.0028536 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
0.771: [GC (Allocation Failure) 0.771: [ParNew: 36035K->1455K(38080K), 0.0059700 secs] 36035K->2699K(122752K), 0.0060435 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
0.955: [GC (Allocation Failure) 0.955: [ParNew: 35311K->1130K(38080K), 0.0324561 secs] 36555K->2375K(122752K), 0.0325557 secs] [Times: user=0.05 sys=0.00, real=0.03 secs]
1.167: [GC (Allocation Failure) 1.167: [ParNew: 34983K->1755K(38080K), 0.0010659 secs] 36228K->3000K(122752K), 0.0011454 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.295: [Full GC (Metadata GC Threshold) 1.295: [CMS: 1244K->2877K(84672K), 0.0339938 secs] 23038K->2877K(122752K), [Metaspace: 9925K->9925K(1058816K)], 0.0341380 secs] [Times: user=0.05 sys=0.02, real=0.03 secs]
1.329: [Full GC (Last ditch collection) 1.329: [CMS: 2877K->1887K(84672K), 0.0146346 secs] 2877K->1887K(122816K), [Metaspace: 9925K->9925K(1058816K)], 0.0147663 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
1.345: [GC (CMS Initial Mark) [1 CMS-initial-mark: 1887K(84672K)] 1887K(122816K), 0.0002645 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.345: [CMS-concurrent-mark-start]
1.348: [CMS-concurrent-mark: 0.003/0.003 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
1.348: [CMS-concurrent-preclean-start]
1.376: [CMS-concurrent-preclean: 0.001/0.028 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
1.378: [Full GC (Metadata GC Threshold) 1.378: [CMS (concurrent mode failure): 1887K->1880K(84672K), 0.0130663 secs] 3226K->1880K(122816K), [Metaspace: 9922K->9922K(1058816K)], 0.0131808 secs] [Times: user=0.02 sys=0.01, real=0.01 secs]
1.391: [Full GC (Last ditch collection) 1.391: [CMS: 1880K->1880K(84672K), 0.0116271 secs] 1880K->1880K(122816K), [Metaspace: 9922K->9922K(1058816K)], 0.0117646 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
Heap
par new generation total 38144K, used 1000K [0x0000000085200000, 0x0000000087b60000, 0x00000000ae0a0000)
eden space 33920K, 2% used [0x0000000085200000, 0x00000000852fa270, 0x0000000087320000)
from space 4224K, 0% used [0x0000000087320000, 0x0000000087320000, 0x0000000087740000)
to space 4224K, 0% used [0x0000000087740000, 0x0000000087740000, 0x0000000087b60000)
concurrent mark-sweep generation total 84672K, used 1880K [0x00000000ae0a0000, 0x00000000b3350000, 0x0000000100000000)
Metaspace used 9953K, capacity 10186K, committed 10240K, reserved 1058816K
class space used 852K, capacity 881K, committed 896K, reserved 1048576K
JVM栈内存溢出的时候,应该如何解决
栈内存溢出而言,我们定位和解决问题非常的简单,你只要把所有的异常都写入本地日志文件,那么当发现系统崩溃了,第一步就去日志里定位一下异常信息就知道了。
具体遇到OOM该怎么处理呢?
- 查看系统日志,里面会有详细的信息(定位到OOM发生在哪个内存区域)-> 主体人
- MAT分析工具分析dump文件
- 根据MAT分析结果去定位框架问题