在正式開始講解關於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異常的具體優化措施。