OOM简记

OOM简记

notice: 下面说的比如10M老年代空间,在10M分配完毕的时候进行FullGC都是简化的说法,其实应该是有个空间分配担保机制的存在,不会出现在10M全部使用的情况下才进行FullGC的情况。

1. OOME出现的区域

  1. heap java堆

  2. vm stack 虚拟机栈

  3. native method stack 本地方法栈

  4. method area 方法区

  5. direct memory 直接内存

ps. 除了program counter register 程序计数器外的其他内存区域都有可能发生oome

2. 怎么判断OOME出现的区域

查看异常堆栈信息,一般在OOME后会进一步跟着提示具体的区域,比如

Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
 at com.cqx.oom.OOMDemo.lambda$main$0(OOMDemo.java:23)
 at com.cqx.oom.OOMDemo$Lambda$1/668386784.run(Unknown Source)
 at java.lang.Thread.run(Thread.java:748)

3. 什么时候会抛出OOME

当创建对象时jvm检测到内存不够本次内存分配,于是会进行一次FullGC,如果本次GC后还是内存不够分配,那就抛出OOME,当前的线程就会死亡。

OOME Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. ----API文档

The JVM will run the GC when it's on edge of the OutOfMemoryError. If the GC didn't help at all, then the JVM will throw OOME.

具体情况可能发生OOME的情况如下

假设jvm的参数设置现在是 -Xmx20M -Xmx20M -Xmn10M -XX:MaxDirectMemorySize=1M, 即分配堆大小为20M,其中老年代和新生代各10M
public static final int _1MB = 1024 * 1024;
1. 持续不断创建对象,并不释放他的引用
for(i = 0; i < 100; i++) {
byte[] M1 = new byte[_1MB];
}
随着对象不断创建,tenured generation到达了10MB,即已经装满了,然后继续创建对象,随着新生代中Eden区再次被填满,触发一次Minor GC,之前Survivor0中部分对象由于各种原因,比如年龄大了,或者本次MinorGC中发现S0不够存放本次存活的对象,所以会有对象晋升到Old Generation。此时JVM发现老年代的内存也不够进行此次分配,于是就进行一次FullGC(Major GC),根据可达性分析,从GC ROOTS开始分析对象的引用关系,发现任然存活,就不进行回收,所有本次FullGC并没有任何卵用,所有GC后还是分配不了,那么此时当前线程就会抛出一个OOME,然后死亡。

2. byte[] _12MB = new byte[12 * _1MB];
大对象的创建会根据JVM参数的设置直接分配到老年代中,跟1类似,FULLGC后发现内存不够,抛出OOME

3. ByteBuffer.allocateDirect(2 * _1MB);
由于之前指定了最大直接内存为1MB 这边分配了2MB就抛出了OOME
java.lang.OutOfMemoryError: Direct buffer memory

4. 方法区OOME
之前在第一次接 Jenkins发布平台时就出现了这个问题。当时发布www?指定的jdk版本是7,可能是因为www大量的jsp文件(感觉也不多),在编译生成class文件时导致方法区不够用发生了OOME。看当时打印的异常日志确认是方法区的OOME。
原因: jdk8之前版本不通过-XX:PermSize和-XX:MaxPermSize显示指定方法区大小,应该就是64MB。当时的jenkins中配置只指定了堆大小,没有指定方法区的大小
解决: 1. -XX:PermSize和-XX:MaxPermSize 2.改用jdk8(移除了使用永久代来当做方法区的策略,使用了metaspace元数据区,不暂用jvm指定的堆内存,而是使用机器的native memory,不受jvm堆的限制)

5. 调用外部服务,外部服务故障或者处理缓慢导致OOME
比如调用中交兴路查询位置接口,网络或者ZJXL服务原因请求一直得不到反馈,两边处理速度不对等,导致越来越多的请求积压,OOME。
思路:
a. 使用hystrix等熔断功能的工具来管理外部服务调用
b. 复用tcp连接。可以考虑加上keepalive:true的请求头,避免每次调用都重新发起tcp连接,
c. httpclient这种应该是要单例的,不需要每次请求都单独创建
d. 生产者消费者模式来处理

6. 内存泄漏
a. 监听器和一些回调注册上来但是没有显示的取消,服务端一直持有这个监听器的引用。
b. 缓存泄漏,比如用HashMap实现的缓存,吧引用扔到进去后忘了,一直扔一直扔就炸了,GC的时候发现对象引用仍然在缓存里,就不回收就炸了。可以用WeakHashMap来实现,key是WeakReference,GC可以回收掉。
</pre>

4. OOME会导致JVM shutdown吗

不会。OOME只会把抛出这个异常的线程给杀掉。 之所以发生OOME的时候应用程序经常出现假死,是因为OOM了,其他正常运行的线程也无法分配到资源,各种请求都无法得到处理,给人一种应用挂掉的感觉。

如果你想在OOME的时候主动kill掉当前的应用可以
-XX:OnOutOfMemoryError="kill -9 %p"  这里还可以执行你的脚本,这样就可以自动重启。。。
%p is the current Java process PID placeholder.

5. 上面说的OOME会杀死当前的线程,那么GC可以回收到这部分资源,释放空间,那么为什么应用程序还是无法响应请求呢。

因为往往抛出OOME的那个线程,不是大头,他只是压垮骆驼的最后一根稻草。

比如一个应用,堆最大设为10M,一个定时任务的线程读取数据,在内存里生成了上万个对象,进行处理,此时用了9.9MB内存空间。然后一个普通的查询请求进来,OOME了,这个请求线程死亡了,由于定时任务线程还在处理,GC只把请求线程的资源回收。那么在定时任务线程处理期间,还是会一直出现OOME。

6. JVM可以自动从OOME恢复吗

可以的,但是不建议。

例如5中的情况,定时任务线程处理完毕了,那么GC会吧这部分内存释放掉,程序又正常了。 但是发生OOME的时候不建议让JVM自动恢复。因为出现了这个异常一定是你程序上有漏洞,有问题,就算本次恢复了,接下去很可能还是会出现这个问题。而且OOM恢复期间很难熬,不管是对于JVM还是用户来说。具体其他的可以看下面的链接

比如之前遇到的,之前做的司机证件导入功能,就发生过一次OOME。这个功能是处理用户导入的照片文件,服务器接收这些文件,处理并存放到MongoDB。当时用户间隔很短时间导入两次大约1G的照片文件,第一次导入的请求还在执行中,因为照片需要读取到内存,照片文件序列化生成的大对象,直接进入老年代,MinorGC没办法清理,占用了大量的内存。紧接着第二次导入请求进来了,在处理过程中由于内存不够OOME了。但是这并不影响第一次的那个请求线程。当时jmap -dump:live,format=b,file=path pid dump了堆栈信息,发现内存够用啊怎么会发生OOME呢,觉得很奇怪。然后我让用户再次导入,发现还是OOME。后面就突然发现-dump:live或者-histo:live会先触发一次FullGC再进行内存信息收集,所以出现异常的那个线程的内存都被回收掉了,所以dump文件上看来是正常的。 所以我就让用户先别导,过个半个小时在来操作,这样就好了。

live指令的解释
dump only live objects; if not specified, all objects in the heap are dumped.</pre>

7. 怎么应对OOME

  1. 考虑在JVM启动参数就设置上-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath,但是有风险,比如5中例子,一直会触发OOME,一直会生成dump文件,会更加麻烦,所以可以考虑加上-XX:OnOutOfMemoryError="kill -9 %p"

  2. 如果1没做,那么可以考虑用jmap来生成dump堆栈快照迅速保留现场信息,并重启应用(1 2G可能几分钟就好了????没找到数据,懒得试)。在此之前可以用jstats查看GC的情况

  3. 如果JVM的分配内存很大,好几G,jmap可能需要执行很久才生成dump文件,可以考虑用gdb来处理,好像会快很多。不管他不是jdk自带的工具,要自己下载。参考链接

  4. 考虑使用softReference、weakReference、weakHashMap等等。

  5. 不用jmap,使用编程式方式触发生成当前的堆转储快照,参考链接

8. 堆转储快照分析

工具: jhat/visualVM/JPofiler/MAT 语句: OQL(对象查询语句)

9. spring actuator/JMX Java Management Extensions

actuator 应用监控的东西,暴露了一些http接口,有个事heapdump还有个stackdump,可以通过定时任务之类去访问然后分析,如果有问题就通知之类的。

jmx 公司有平台radar。可以多学习下这方面的东西。

10. 死锁与死循环,CPU飙升相关排查

  1. jstack 看线程堆栈

  2. top查找cpu占用率高的pid top -p pid -H 查看进程内线程的pid

  3. pid转16进制,去jstack中查日志

https://blog.51cto.com/13732225/2347907

. . . . . .

参考的文档链接

https://stackoverflow.com/questions/3058198/can-the-jvm-recover-from-an-outofmemoryerror-without-a-restart

https://stackoverflow.com/questions/12096403/java-shutting-down-on-out-of-memory-error

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

https://segmentfault.com/a/1190000010603813

https://stackoverflow.com/questions/6418089/does-jmap-force-garbage-collection-when-the-live-option-is-used

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容