点赞+收藏 就学会系列,文章收录在 GitHub JavaKeeper ,N线互联网开发必备技能兵器谱,笔记自取

在《Java虚拟机规范》的规定里,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError 异常的可能。

本篇主要包括如下 OOM 的介绍和示例:

  • java.lang.StackOverflowError
  • java.lang.OutOfMemoryError: Java heap space
  • java.lang.OutOfMemoryError: GC overhead limit exceeded
  • java.lang.OutOfMemoryError-->Metaspace
  • java.lang.OutOfMemoryError: Direct buffer memory
  • java.lang.OutOfMemoryError: unable to create new native thread
  • java.lang.OutOfMemoryError:Metaspace
  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  • java.lang.OutOfMemoryError: Out of swap space
  • java.lang.OutOfMemoryError:Kill process or sacrifice child

我们常说的 OOM 异常,其实是 Error

一. StackOverflowError

1.1 写个 bug

public class StackOverflowErrorDemo {

    public static void main(String[] args) {
javaKeeper();
} private static void javaKeeper() {
javaKeeper();
}
}

上一篇详细的介绍过JVM 运行时数据区,JVM 虚拟机栈是有深度的,在执行方法的时候会伴随着入栈和出栈,上边的方法可以看到,main 方法执行后不停的递归,迟早把栈撑爆了

Exception in thread "main" java.lang.StackOverflowError
at oom.StackOverflowErrorDemo.javaKeeper(StackOverflowErrorDemo.java:15)

1.2 原因分析

  • 无限递归循环调用(最常见原因),要时刻注意代码中是否有了循环调用方法而无法退出的情况
  • 执行了大量方法,导致线程栈空间耗尽
  • 方法内声明了海量的局部变量
  • native 代码有栈上分配的逻辑,并且要求的内存还不小,比如 java.net.SocketInputStream.read0 会在栈上要求分配一个 64KB 的缓存(64位 Linux)

1.3 解决方案

  • 修复引发无限递归调用的异常代码, 通过程序抛出的异常堆栈,找出不断重复的代码行,按图索骥,修复无限递归 Bug
  • 排查是否存在类之间的循环依赖(当两个对象相互引用,在调用toString方法时也会产生这个异常)
  • 通过 JVM 启动参数 -Xss 增加线程栈内存空间, 某些正常使用场景需要执行大量方法或包含大量局部变量,这时可以适当地提高线程栈空间限制

二. Java heap space

Java 堆用于存储对象实例,我们只要不断的创建对象,并且保证 GC Roots 到对象之间有可达路径来避免 GC 清除这些对象,那随着对象数量的增加,总容量触及堆的最大容量限制后就会产生内存溢出异常。

Java 堆内存的 OOM 异常是实际应用中最常见的内存溢出异常。

2.1 写个 bug

/**
* JVM参数:-Xmx12m
*/
public class JavaHeapSpaceDemo { static final int SIZE = 2 * 1024 * 1024; public static void main(String[] a) {
int[] i = new int[SIZE];
}
}

代码试图分配容量为 2M 的 int 数组,如果指定启动参数 -Xmx12m,分配内存就不够用,就类似于将 XXXL 号的对象,往 S 号的 Java heap space 里面塞。

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at oom.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:13)

2.2 原因分析

  • 请求创建一个超大对象,通常是一个大数组
  • 超出预期的访问量/数据量,通常是上游系统请求流量飙升,常见于各类促销/秒杀活动,可以结合业务流量指标排查是否有尖状峰值
  • 过度使用终结器(Finalizer),该对象没有立即被 GC
  • 内存泄漏(Memory Leak),大量对象引用没有释放,JVM 无法对其自动回收,常见于使用了 File 等资源没有回收

2.3 解决方案

针对大部分情况,通常只需要通过 -Xmx 参数调高 JVM 堆内存空间即可。如果仍然没有解决,可以参考以下情况做进一步处理:

  • 如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全部结果,而没有做结果数限制
  • 如果是业务峰值压力,可以考虑添加机器资源,或者做限流降级。
  • 如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接

面试官:说说内存泄露和内存溢出

加送个知识点,三连的终将成为大神~~

内存泄露和内存溢出

内存溢出(out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个 Integer,但给它存了 Long 才能存下的数,那就是内存溢出。

内存泄露( memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak 最终会导致 out of memory!

三、GC overhead limit exceeded

JVM 内置了垃圾回收机制GC,所以作为 Javaer 的我们不需要手工编写代码来进行内存分配和释放,但是当 Java 进程花费 98% 以上的时间执行 GC,但只恢复了不到 2% 的内存,且该动作连续重复了 5 次,就会抛出 java.lang.OutOfMemoryError:GC overhead limit exceeded 错误(俗称:垃圾回收上头)。简单地说,就是应用程序已经基本耗尽了所有可用内存, GC 也无法回收。

假如不抛出 GC overhead limit exceeded 错误,那 GC 清理的那么一丢丢内存很快就会被再次填满,迫使 GC 再次执行,这样恶性循环,CPU 使用率 100%,而 GC 没什么效果。

3.1 写个 bug

出现这个错误的实例,其实我们写个无限循环,往 List 或 Map 加数据就会一直 Full GC,直到扛不住,这里用一个不容易发现的栗子。我们往 map 中添加 1000 个元素。

/**
* JVM 参数: -Xmx14m -XX:+PrintGCDetails
*/
public class KeylessEntry { static class Key {
Integer id; Key(Integer id) {
this.id = id;
} @Override
public int hashCode() {
return id.hashCode();
}
} public static void main(String[] args) {
Map m = new HashMap();
while (true){
for (int i = 0; i < 1000; i++){
if (!m.containsKey(new Key(i))){
m.put(new Key(i), "Number:" + i);
}
}
System.out.println("m.size()=" + m.size());
}
}
}
...
m.size()=54000
m.size()=55000
m.size()=56000
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

从输出结果可以看到,我们的限制 1000 条数据没有起作用,map 容量远超过了 1000,而且最后也出现了我们想要的错误,这是因为类 Key 只重写了 hashCode() 方法,却没有重写 equals() 方法,我们在使用 containsKey() 方法其实就出现了问题,于是就会一直往 HashMap 中添加 Key,直至 GC 都清理不掉。

10种常见OOM分析——手把手教你写bug的更多相关文章

  1. Linux下命令行cURL的10种常见用法示例

    curl的命令行工具功能非常强大,这些数据交互的功能基本上都是通过URL方式进行的,下面这篇文章主要给大家分享了在Linux中命令行cURL的10种常见用法示例,通过示例代码介绍的非常详细,需要的朋友 ...

  2. 浅析10种常见的黑帽seo手法

    虽然博主并不认同黑帽seo手法,但是一些常见的黑帽手法还是需要了解的,增加自己对黑帽的认知,也可以在自己优化网站时适时的规避开这些黑帽手法,从而避免自己的网站被搜索引擎惩罚.好了,话不多说,下面进入今 ...

  3. 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接

    本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...

  4. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  5. 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...

  6. [原创]手把手教你写网络爬虫(4):Scrapy入门

    手把手教你写网络爬虫(4) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花! 咦?怎么有人扔西红柿 ...

  7. [原创]手把手教你写网络爬虫(5):PhantomJS实战

    手把手教你写网络爬虫(5) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 大家好!从今天开始,我要与大家一起打造一个属于我们自己的分布式爬虫平台,同时也会对涉及到的技术进行详细介绍.大 ...

  8. [原创]手把手教你写网络爬虫(7):URL去重

    手把手教你写网络爬虫(7) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 本期我们来聊聊URL去重那些事儿.以前我们曾使用Python的字典来保存抓取过的URL,目的是将重复抓取的UR ...

  9. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

随机推荐

  1. Swagger之外的选择

    今天给大家安利一款接口文档生成器--JApiDocs. swagger想必大家都用过吧,非常方便,功能也十分强大.如果要说swaager有什么缺点,想必就是注解写起来比较麻烦.如果我说有一款不用写注解 ...

  2. 信息收集-DNS

    首先更正一个小白很普遍的错误观点,www.baidu.com(严格上是www.baidu.com. 这个点是根的意思,所有的记录从这里开始)并不是一个真正意义上的域名,而是百度服务器的A记录,baid ...

  3. Python 简明教程 ---10,Python 列表

    微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 程序 = 算法 + 数据结构 -- Nicklaus Wirth 目录 从这句话程序 = 算法 + ...

  4. 入门大数据---Hbase的SQL中间层_Phoenix

    一.Phoenix简介 Phoenix 是 HBase 的开源 SQL 中间层,它允许你使用标准 JDBC 的方式来操作 HBase 上的数据.在 Phoenix 之前,如果你要访问 HBase,只能 ...

  5. Mybatis一对一映射resultMap子标签中顺序问题

    直接上图 鼠标点上红线出现如下提示 The content of element type "resultMap" must match  "(constructor?, ...

  6. 《The Google File System》论文研读

    GFS 论文总结 说明:本文为论文 <The Google File System> 的个人总结,难免有理解不到位之处,欢迎交流与指正 . 论文地址:GFS Paper 阅读此论文的过程中 ...

  7. node实现文件属性批量修改(文件名)

    前言 书接上回,我们实现了批量修改文件的时间,但是却没有实现文件名称的批量修改,是因为我也说过,没有界面的话直接在命令行实现显得有点繁琐,所以我们就通过接口+界面的方式来实现我们这个小需求吧.所以,闲 ...

  8. 每日一题 - 剑指 Offer 40. 最小的k个数

    题目信息 时间: 2019-06-30 题目链接:Leetcode tag: 快排 难易程度:中等 题目描述: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3. ...

  9. 深入理解letter-spacing,word-spacing的对比区别

    letter-spacing lletter-spacing 属性增加或减少字符间的空白(字符间距). 该属性定义了在文本字符框之间插入多少空间.由于字符字形通常比其字符框要窄,指定长度值时,会调整字 ...

  10. 使用flex实现5种常用布局

    Sticky Footer 经典的上-中-下布局. 当页面内容高度小于可视区域高度时,footer 吸附在底部:当页面内容高度大于可视区域高度时,footer 被撑开排在 content 下方 dem ...