在正式开始讲解关于OutOfMemoryError错误以前先来了解下,我在遇到这个异常的背景。java
我须要对hive中的数据进行批量操做处理,对于没有了解过hive的同窗来讲,有点茫然了。因而按照常规思路开始经过JDBC链接Hive读取数据 -> 处理数据 -> 写入数据。编程
貌似没有什么不妥的,因而乎噼里啪啦代码写好了 -> 调试 -> 验证 -> 上线批量处理数据。上线处理数据的过程出问题了,为何半天没有处理完一条数据?分布式
被坑了,Hive可不是MySQL可以在ms内返回结果给你,这下傻了,处理一条数据须要几分钟到十几分钟不等,简直不能忍啊。post
不能忍了能怎么办,能怎么办优化解决方案呗。而是借助Hive分布式MR的能力将输入数据先处理一遍按照读取的顺序给整理好存放到中间表 -> 批量操做中间表 -> 数据写回。优化
开始接近了本篇要讲解的主题了,进行批量操做数据而致使OutOfMemoryError。spa
相信有必定编程经验的开发人员都会遇到这个错误,其实出现这个错误你们确定想到的缘由:是否是程序写的有问题产生了大量垃圾对象无法被JVM回收掉,亦或者是程序的正常逻辑确实须要用到比JVM提供的堆区内存大。.net
本人在遇到这个错误的时候也是这么怀疑过,因而首先去检查了下本身的代码,由于逻辑代码比较少仔细分析后发现程序写的没问题,不该该出现没法被JVM回收的内存垃圾。那怎么验证本身的代码没有出现内存垃圾?线程
经过 jmap -histo:live <pid>
能够分析出全部存活的对象所占用的内存大小。调试
code
发如今跳出批量处理数据的逻辑后,全部相关的内存都被回收了,因此确认没有内存垃圾。
那就只能是处理的数据超过了JVM堆区内存上限,按照这个猜想往下分析。
首先来观察下机器内存的变化状况jstat -gc <pid> 5000 100
后面两个参数一个是间隔多久统计一次,总共统计多少次。不了解能够自行搜索资料了解这个命令的输出参数。

确实内存会在一段时间后大量释放,而后随着运行又将整个堆区给占满了。到这里能够肯定是因为批量处理数据太多而使线程所拥有的堆区撑爆了。
原本分析应该到这里为止了,可是得知道是什么将堆区给占满了吧?
首先经过Java对象内存存储结构这篇文章了解Java一个对象在内存中分配的字节数为多少。
经过jmap -histo:live <pid>
查看在内存满和不满的时候其中存活的对象。

主要暴增对象如上图框出来的地方。
TestObject定义以下:
public class TestObject { private String a; private String b; private String c; private String d; private Integer e; private Integer f; }
从TestObject的定义和上图存活对象的对比就能够判断出java.lang.String、java.lang.Integer、[C暴增的缘由了。
TestObject size = 194664000/4866600 = 40
String size = 514238640/21426610 = 24
符合Java对象内存一篇文中分析的字节大小。
JVM内存管理不少前辈都已经讲的很是清楚了,根据理论咱们来实际窥探下JVM是如何针对内存进行管理的,同时如何进行内存回收。

根据JVM对内存的使用策略咱们能够看到程序不断使用内存的过程当中堆内存容量在各个部分的波动状况,在新生代/老生代内存达到必定百分比的同时GC回收的回收状况。
触发条件:
老生代:1043574/1329152 = 0.78 (-XX:CMSInitiatingOccupancyFraction 参数控制容量达到多少进行FGC)
新生代:在Eden区满后触发
针对OutOfMemoryError优化手段无非两种:
针对OutOfMemoryError异常的具体优化措施。