一、MapReduce作業運作過程中記憶體溢出錯誤分類
1、Mapper/Reducer階段JVM記憶體溢出(一般都是堆)
1)JVM堆(Heap)記憶體溢出:堆記憶體不足時,一般會抛出如下異常: 第一種:“java.lang.OutOfMemoryError:” GC overhead limit exceeded; 第二種:“Error: Java heapspace”異常資訊; 第三種:“running beyondphysical memory limits.Current usage: 4.3 GB of 4.3 GBphysical memory used; 7.4 GB of 13.2 GB virtual memory used. Killing container”。
2)棧記憶體溢出:抛出異常為:java.lang.StackOverflowError 常會出現在SQL中(SQL語句中條件組合太多,被解析成為不斷的遞歸調用),或MR代碼中有遞歸調用。這種深度的遞歸調用在棧中方法調用鍊條太長導緻的。出現這種錯誤一般說明程式寫的有問題。
2、MRAppMaster記憶體不足
如果作業的輸入的資料很大,導緻産生了大量的Mapper和Reducer數量,緻使MRAppMaster(目前作業的管理者)的壓力很大,最終導緻MRAppMaster記憶體不足,作業跑了一般出現了OOM資訊 異常資訊為: Exception: java.lang.OutOfMemoryError thrown from theUncaughtExceptionHandler in thread "Socket Reader #1 for port 30703 Halting due to Out Of Memory Error... Halting due to Out Of Memory Error... Halting due to Out Of Memory Error...
3、非JVM記憶體溢出
異常資訊一般為:java.lang.OutOfMemoryError:Direct buffer memory 自己申請使用作業系統的記憶體,沒有控制好,出現了記憶體洩露,導緻的記憶體溢出。
二、錯誤解決參數調優
1、Mapper/Reducer階段JVM堆記憶體溢出參數調優
目前MapReduce主要通過兩個組參數去控制記憶體:(将如下參數調大)
Maper
注意:因為在yarn container這種模式下,map/reduce task是運作在Container之中的,是以上面提到的mapreduce.map(reduce).memory.mb大小都大于mapreduce.map(reduce).java.opts值的大小。mapreduce.{map|reduce}.java.opts能夠通過Xmx設定JVM最大的heap的使用,一般設定為0.75倍的memory.mb,因為需要為java code等預留些空間
2、MRAppMaster:
yarn.app.mapreduce.am.command-opts=-Xmx1024m(預設參數,表示jvm堆記憶體) yarn.app.mapreduce.am.resource.mb=1536(container的記憶體) 注意在Hive ETL裡面,按照如下方式設定: set mapreduce.map.child.java.opts="-Xmx3072m"(注:-Xmx設定時一定要用引号,不加引号各種錯誤) set mapreduce.map.memory.mb=3288 或 set mapreduce.reduce.child.java.opts="xxx" set mapreduce.reduce.memory.mb=xxx 涉及YARN參數:
•yarn.scheduler.minimum-allocation-mb (最小配置設定機關1024M) •yarn.scheduler.maximum-allocation-mb (8192M) •yarn.nodemanager.vmem-pmem-ratio (虛拟記憶體和實體記憶體之間的比率預設 2.1) •yarn.nodemanager.resource.memory.mbYarn的ResourceManger(簡稱RM)通過邏輯上的隊列配置設定記憶體,CPU等資源給application,預設情況下RM允許最大AM申請Container資源為8192MB(“
yarn.scheduler.maximum-allocation-mb“),預設情況下的最小配置設定資源為1024M(“
yarn.scheduler.minimum-allocation-mb“),AM隻能以增量(”
yarn.scheduler.minimum-allocation-mb“)和不會超過(“
yarn.scheduler.maximum-allocation-mb“)的值去向RM申請資源,AM負責将(“
mapreduce.map.memory.mb“)和(“
mapreduce.reduce.memory.mb“)的值規整到能被(“
yarn.scheduler.minimum-allocation-mb“)整除,RM會拒絕申請記憶體超過8192MB和不能被1024MB整除的資源請求。(不同配置會有不同) Yarn的工作流程請參考
錯誤模拟和分析
一、錯誤模拟(JVM架構和GC垃圾回收機制)
(1) 堆記憶體溢出

(2) 棧記憶體溢出
(3) 方法區記憶體溢出
(4) 非JVM記憶體溢出
二、原因分析(歸根揭底都是GC導緻的) 1)JVM的三大核心區域
2)JVMHeap區域(年輕代、老年代)和方法區(永久代)結構圖
3)通過一段代碼分析GC的整個過程:
public class HelloJVM {
//在JVM運作的時候會通過反射的方式到Method區域找到入口方法main
public static void main(String[] args) {//main方法也是放在Method方法區域中的
/**
* student(小寫的)是放在主線程中的Stack區域中的
* Student對象執行個體是放在所有線程共享的Heap區域中的
*/
Student student = new Student("spark");
/**
* 首先會通過student指針(或句柄)(指針就直接指向堆中的對象,句柄表明有一個中間的,student指向句柄,句柄指向對象)
* 找Student對象,當找到該對象後會通過對象内部指向方法區域中的指針來調用具體的方法去執行任務
*/
student.sayHello();
}
}
class Student {
// name本身作為成員是放在stack區域的但是name指向的String對象是放在Heap中
private String name;
public Student(String name) {
this.name = name;
}
//sayHello這個方法是放在方法區中的
public void sayHello() {
System.out.println("Hello, this is " + this.name);
}
}
從Java GC的角度解讀代碼:程式20行new的Person對象會首先會進入年輕代的Eden中(如果對象太大可能直接進入年老代)。在GC之前對象是存在Eden和from中的,進行GC的時候Eden中的對象被拷貝到To這樣一個survive空間(
survive(
幸存)空間:包括from和to,他們的空間大小是一樣的,又叫s1和s2)中(有一個拷貝算法),From中的對象(算法會考慮經過GC幸存的次數)到一定次數(門檻值(如果說每次GC之後這個對象依舊在Survive中存在,GC一次他的Age就會加1,預設15就會放到Old Generation。但是實際情況比較複雜,有可能沒有到門檻值就從Survive區域直接到Old Generation區域。在進行GC的時候會對Survive中的對象進行判斷,Survive空間中有一些對象Age是一樣的,也就是經過的GC次數一樣,年齡相同的這樣一批對象的總和大于等于Survive空間一半的話,這組對象就會進入old Generation中,(是一種動态的調整))),會被複制到Old Generation,如果沒到次數From中的對象會被複制到To中,複制完成後To中儲存的是有效的對象,Eden和From中剩下的都是無效的對象,這個時候就把Eden和From中所有的對象清空。在複制的時候Eden中的對象進入To中,To可能已經滿了,這個時候Eden中的對象就會被直接複制到OldGeneration中,From中的對象也會直接進入Old Generation中。就是存在這樣一種情況,To比較小,第一次複制的時候空間就滿了,直接進入old Generation中。複制完成後,To和From的名字會對調一下,因為Eden和From都是空的,對調後Eden和To都是空的,下次配置設定就會配置設定到Eden。一直循環這個流程。好處:使用對象最多和效率最高的就是在Young G
eneration中,通過From to就避免過于頻繁的産生Full GC(Old Generation滿了一般都會産生FullGC)虛拟機在進行MinorGC(新生代的GC)的時候,會判斷要進入Old Generation區域對象的大小,是否大于Old Generation剩餘空間大小,如果大于就會發生Full GC。 剛配置設定對象在Eden中,如果空間不足嘗試進行GC,回收空間,如果進行了MinorGC空間依舊不夠就放入Old Generation,如果OldGeneration空間還不夠就OOM了。 比較大的對象,數組等,大于某值(可配置)就直接配置設定到老年代,(避免頻繁記憶體拷貝)
年輕代和年老代屬于Heap空間的 Permanent Generation(永久代)可以了解成方法區,(它屬于方法區)也有可能發生GC,例如類的執行個體對象全部被GC了,同時它的類加載器也被GC掉了,這個時候就會觸發永久代中對象的GC。如果OldGeneration滿了就會産生FullGC