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:
mapreduce.map.java.opts=-Xmx2048m(默認參數,表示jvm堆內存,注意是mapreduce不是mapred)
mapreduce.map.memory.mb=2304(container的內存)
Reducer:
mapreduce.reduce.java.opts=-=-Xmx2048m(默認參數,表示jvm堆內存)
mapreduce.reduce.memory.mb=2304(container的內存)
注意:因爲在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.mb
Yarn的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整除的資源請求。(不同配置會有不同)
錯誤模擬和分析
一、錯誤模擬(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 Generation中,通過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