前言

如今不管是在面试还是在我们的工作中,OOM总是不断的出现在我们的视野中,所以我们有必要去了解一下导致OOM的原因以及一些基本的调整方法,大家可以通过下面的事例来了解一下什么样的代码会导致OOM,帮助我们以后在工作中能够通过异常信息来判断是JVM里面哪个区域出现了问题。

先介绍一下笔者的相关编码环境。

jdk:java version "1.8.0_121"

ide:IntelliJ IDEA 2019.1 (Community Edition)


正文

1、Java堆溢出

Java中的堆存储的都是对象实例,当我们不断的创建对象,而GC的时候又不能回收,当存储的对象大小超过了-Xmx的值,这时候则会出现OutOfMemoryError.[-XX:+HeapDumpOnOutOfMemoryError]参数可以让jvm出现内存溢出的时候dump出内存堆转储快照。

/**
* VM Args: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
* @author wangzenghuang
*/
public class HeapOOMDemo {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
while(true){
stringList.add("str");
}
}
}

运行结果,发生OOM,并且在我们项目的根目录dump出当前的内存堆快照

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1376.hprof ...
Heap dump file created [7972183 bytes in 0.047 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 HeapOOMDemo.main(HeapOOMDemo.java:12) Process finished with exit code 1

简单解决思路

那么发生这个问题以后我们的解决思路有哪些呢?我们可以利用一些工具(例如Eclipse Memory Analyzer )来分析dump出的文件,一般来说,当生产环境发生OOM,比较常见的一个原因是发生了内存泄漏,用工具可以分析出泄露的对象到GC Root的引用链,从而定位到问题代码。假如经过分析后发现内存中的对象都是“必须存活”的对象,这时候就要思考下项目中是否把“-Xms跟-Xmx”设置得太小了(当然这里也不是随意调大,需要结合机器的物理内存情况),再者需要留意代码中是否有一些长生命周期的对象,从代码中优化内存消耗。

2、方法区溢出

在jvm的方法区中,它主要存放了类的信息,常量,静态变量等。在jdk8以前是通过“-XX:PermSize,-XX:MaxPermSize”来调整这个区域的值,但是从8开始呢,永久代的概念被MetaSpace(元空间)代替了,对应的参数也变成了“-XX:MetaspaceSize,-XX:MaxMetaspaceSize”。在这个例子中使用CGLib来动态生成一些类,方便我们实验操作。

/**
* VM Args: -XX:MetaspaceSize=5m -XX:MaxMetaspaceSize=5m
* @author wangzenghuang
*/
public class MethodAreaOOMDemo {
public static void main(String[] args) {
while(true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(obj,objects);
}
});
enhancer.create();
}
}
static class OOMObject{}
}

运行结果

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

简单解决方法

这个问题的话,一般来说根据情况调整方法区的大小就行了,网上也有人说可以去掉MetaSpace的的大小限制,但是不建议这么干,毕竟不可控的事情我们要少点干,很容易给自己埋雷。

3.栈溢出

对于我们来说,还有一个熟悉的错误,那就是“StackOverflowError”,它是由线程请求的栈深度超过了jvm允许的最大范围而产生的。“-Xss”参数可以设置栈容量。

/**
* VM Args: -Xss128k
* @author wangzenghuang
*/
public class StackOFDemo {
private static int stackLength = 1; public void stackLeak(){
stackLength++;
stackLeak();
} public static void main(String[] args) {
StackOFDemo stackOFDemo = new StackOFDemo();
try {
stackOFDemo.stackLeak();
}catch (Throwable e){
System.out.println("length : "+ stackLength);
throw e;
}
}
}

运行结果

length : 983
Exception in thread "main" java.lang.StackOverflowError
at StackOFDemo.stackLeak(StackOFDemo.java:10)
at StackOFDemo.stackLeak(StackOFDemo.java:10)
...

简单解决思路

一般来说此类问题多出现在存在递归的地方,要从代码里重新审视递归未结束的原因,若递归的方法没问题可以根据实际情况调整“-Xss”参数的大小。还有一些代码的循坏依赖也会造成此类情况,

4.直接内存溢出

本机直接内存默认与“-Xmx”设定的值一样大,可以通过“-XX:MaxDirectMemorySize”修改。

/**
* VM Args: -Xmx20m -XX:MaxDirectMemorySize=10
* @author wangzenghuang
*/
public class DirectMemoryOOMDemo {
private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws IllegalAccessException {
Field field = Unsafe.class.getDeclaredFields()[0];
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
while (true){
unsafe.allocateMemory(_1MB);
}
}
}

运行结果

呃,一运行这段代码idea直接闪退了,查阅其他资料可以得知当DirectMemory导致内存溢出时,Heap Dump文件是很小的,如果程序中有使用NIO的情况可以检查一下。

总结 

这里所展示的代码只是可以触发jvm的各种错误,但是并不代表这是唯一的触发错误的方方式,假如我们的代码比较复杂,有时候遇到类似错误的时候还是需要耐心分析。

该文章首发于微信公众号《深夜里的程序猿》,转载请注明出处,侵权必究。

论JVM爆炸的几种姿势及自救方法的更多相关文章

  1. 论JVM爆炸的几种姿势及自救方法,你不得不知!

    前言 如今不管是在面试还是在我们的工作中,OOM总是不断的出现在我们的视野中,所以我们有必要去了解一下导致OOM的原因以及一些基本的调整方法,大家可以通过下面的事例来了解一下什么样的代码会导致OOM, ...

  2. 【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树

    直接上代码 正所谓 人傻自带大常数 平衡树的几种姿势:  AVL Red&Black_Tree 码量爆炸,不常用:SBT 出于各种原因,不常用. 常用: Treap 旋转 基于旋转操作和随机数 ...

  3. 快速了解IOC的几种姿势

    一.首先我们了解IOC如何注入的几种姿势 构造函数注入(Constructor Injection) Ioc容器会智能的选择和调用合适的构造函数以创建依赖的对象.如果被选择的构造函数具有相应的参数,I ...

  4. 两个文件去重的N种姿势

    最近利用shell帮公司优化挖掘关键词的流程,用shell替代了多个环节的操作,极大提高了工作效率. shell在文本处理上确有极大优势,比如多文本合并.去重等,但是最近遇到了一个难搞的问题,即两个大 ...

  5. 又拍云张聪:OpenResty 动态流控的几种姿势

    2019 年 1 月 12 日,由又拍云.OpenResty 中国社区主办的 OpenResty × Open Talk 全国巡回沙龙·深圳站圆满结束,又拍云首席架构师张聪在活动上做了< Ope ...

  6. 补习系列(7)-springboot 实现拦截的五种姿势

    目录 简介 姿势一.使用 Filter 接口 1. 注册 FilterRegistrationBean 2. @WebFilter 注解 姿势二.HanlderInterceptor 姿势三.@Exc ...

  7. python打开文件的N种姿势

    # python打开文件的N种姿势 print('[1]使用open()函数+简单for循环') f1 = open('python.txt') for line in f1: print(line. ...

  8. [转]地图投影的N种姿势

    此处直接给出原文链接: 1.地图投影的N种姿势 2.GIS理论(墨卡托投影.地理坐标系.地面分辨率.地图比例尺.Bing Maps Tile System)

  9. SpringBoot系列教程web篇Servlet 注册的四种姿势

    原文: 191122-SpringBoot系列教程web篇Servlet 注册的四种姿势 前面介绍了 java web 三要素中 filter 的使用指南与常见的易错事项,接下来我们来看一下 Serv ...

随机推荐

  1. tomcat项目绑定到域名及运行内存配置

    一.tomcat中的项目绑定到域名通过域名访问 1.在tomcat下的conf/server.xml中找到Host修改(1.name为你的域名,2.配置Context中的path为空就是直接访问项目不 ...

  2. Java 面试知识点解析(二)——高并发编程篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  3. Scrapy爬取豆瓣电影top250的电影数据、海报,MySQL存储

    从GitHub得到完整项目(https://github.com/daleyzou/douban.git) 1.成果展示 数据库 本地海报图片 2.环境 (1)已安装Scrapy的Pycharm (2 ...

  4. 百度搜索(jsonp)

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>百 ...

  5. 工作中常用的JS函数整理分享(欢迎大家补充)

    今年在渣X工作整理的常用JS函数 今年来了渣X工作,我所在这个部门分工很奇怪,CSS竟然有专门的人在搞,开发PHP的人员需要处理JS,以至于有时候开发起来不是那么得心应手,感觉把JS和CSS拆开就像是 ...

  6. SSM-MyBatis-09:Mybatis中SqlSession的close为什么能造成事务的回滚

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 经过上几次的查找,笔者我就简单的说一下查找的思路,留给读者自己实践 同样找到sqlsession的实现类,-- ...

  7. Activity的生命之路

    activity的生命周期这张图是最经典的了,下面我就说一下 这张图的脉络: 第一条线我们这么走 onCreate→onStart→onResume→onPause→onStop→onDestroy ...

  8. 使用Spring Session实现Spring Boot水平扩展

    小编说:本文使用Spring Session实现了Spring Boot水平扩展,每个Spring Boot应用与其他水平扩展的Spring Boot一样,都能处理用户请求.如果宕机,Nginx会将请 ...

  9. MongoDB中文档操作(二)

    一.插入文档  1.db.集合名.insert()   插入一个:db.user.insert({name:"Join",age:13,address:"beijing& ...

  10. 【BZOJ 3697】采药人的路径

    题目链接: TP 题解: 调了好久233. 大概想一想就是树分,然后考虑这样路径(u,v)的特征,以根节点(root)切开,u到root的阴阳差值,和v到root巧合互为相反数,然后考虑要有一个点可作 ...