5步避免Java堆空間錯誤
牢記以下五個步驟可以為你減少很多頭痛的問題并且避免Java堆空間錯誤。
- 通過計算預(yù)期的內(nèi)存消耗。
- 檢查JVM是否有足夠的可用空間。
- 檢查JVM的設(shè)置是否正確。
- 限制節(jié)點使用交換空間和內(nèi)存分頁。
- 設(shè)置實例slot數(shù)量小于JobTracker web GUI計算的數(shù)值。
譯者注:slot :slot不是CPU的Core,也不是memory chip,它是一個邏輯概念,一個節(jié)點的slot的數(shù)量用來表示某個節(jié)點的資源的容量或者說是能力的大小,因而slot是 Hadoop的資源單位。詳見這里。
在這篇博文里,我將詳細講解每個步驟,幫助大家更好地理解并正確管理實例(task attempt)內(nèi)存。
譯者注:實例(task attempt) :這個詞在官方文檔中找到了解釋: “Each task attempt is one particular instance of a Map or Reduce Task identified by its TaskID”。
理解怎樣管理實例內(nèi)存是很重要的,這樣可以避免Java堆空間錯誤。當運行 map/reduce 作業(yè)(Job)時,你可能會看到實例出現(xiàn)這樣的錯誤:
- 13/09/20 08:50:56 INFO mapred.JobClient: Task Id : attempt_201309200652_0003_m_000000_0, Status : FAILED on node node1
- Error: Java heap space
當試圖申請一個超過Java虛擬機(JVM)設(shè)置的最大內(nèi)存限制時就會發(fā)生這個錯誤。
避免Java堆空間錯誤的第一步是了解你的map和reduce任務(wù)的內(nèi)存需求,以便于你啟動一個JVM時設(shè)置了適當內(nèi)存限制。
例如,hadoop-0.20.2-dev-examples.jar中的wordcount 功能。 不管處理什么數(shù)據(jù),map 任務(wù)都不需要很多內(nèi)存。唯一需要很多內(nèi)存的就是在加載運行所需的函數(shù)庫的時候。當使用默認附帶MapR包的wordcount功能時,512MB的內(nèi)存對于實例JVM是綽綽有余了。如果你打算運行我們提供的Hadoop示例,可以嘗試將map實例JVM的內(nèi)存限制設(shè)為512MB。
如果你知道自己的map實例需要多少內(nèi)存(在本例中是512MB), 那么下一步啟動設(shè)置好JVM內(nèi)存。該實例在JVM中的內(nèi)存是由TaskTracker為Map/Reduce作業(yè)處理數(shù)據(jù)而設(shè)定的。 TaskTracker設(shè)定的限制可能有兩個來源:要么是用戶提交作業(yè)時指定了內(nèi)存大小作為該作業(yè)配置對象的一部分,或者是TaskTracker產(chǎn)生了 默認內(nèi)存大小的JVM。
mapred.map.child.java.opts屬性被用來為TaskTracker 啟動JVM和執(zhí)行map任務(wù)的參數(shù)(在reduce任務(wù)中也有個類似的屬性)。如果mapred.map.child.java.opts屬性被設(shè)置成“-Xmx512m”,那么map實例JVMs會有512MB的內(nèi)存限制。相反的,如果-Xmx沒有通過配置屬性去指定一個數(shù)值的話,那么 每個TaskTracker將會為啟動JVM計算一個默認的內(nèi)存限制。該限制是基于TaskTracker為map/reduce task slot分配的數(shù)量所決定的,并且TaskTracker分配給Map/Reduce總內(nèi)存不能超過系統(tǒng)限制。
TaskTracker為map/reduce實例分配的slot數(shù)量在TaskTracker啟動時就設(shè)定好了。通過每個節(jié)點上mapred-site.xml文件中兩個參數(shù)進行控制的:
- mapred.tasktracker.map.tasks.maximum
- mapred.tasktracker.reduce.tasks.maximum
設(shè)置這些默認值的規(guī)則是基于節(jié)點上CPU核心的數(shù)量。不過你可以下面兩個方法來重載參數(shù):
- 修改mapred-site.xml文件設(shè)定一個固定的slots數(shù)值。
- 使用自定義規(guī)則。
在系統(tǒng)中,TaskTracker map/reduce實例內(nèi)存限制是在TaskTracker進程啟動時設(shè)定的。有兩個地方可以設(shè)置內(nèi)存限制。首先在Hadoop conf目錄下的hadoop-env.sh腳本中可以顯式的設(shè)置,你可以添加下面這行來指定內(nèi)存限制:
- export HADOOP_HEAPSIZE=2000
這行命令限制了節(jié)點上的所有實例JVM總共可以使用2000MB的內(nèi)存。如果沒有在hadoop-env.sh文件中指定 HADOOP_HEAPSIZE這個參數(shù),那么當MapR warden service啟動TaskTracker時會對內(nèi)存進行限制。 warden service會基于節(jié)點上物理內(nèi)存的數(shù)量減去服務(wù)運行中已經(jīng)占用的內(nèi)存數(shù)量得出限制的大小。如果你去看看warden.conf你會看到像這樣的一些屬性:
- service.command.mfs.heapsize.percent=20
- service.command.mfs.heapsize.min=512
這個例子表示,warden占用分配給MFS服務(wù)節(jié)點的20%物理內(nèi)存或最低512MB(512MB<20%的物理內(nèi)存的情況下)。如果你考慮所有服務(wù)都配置在一個節(jié)點上運行的話,你要考慮下在 warden.conf中指定下內(nèi)存分配。你應(yīng)該能明確多少內(nèi)存用于服務(wù)配置(還要為系統(tǒng)正常運行預(yù)留內(nèi)存)。剩下的內(nèi)存就是TaskTracker為并發(fā)運行實例設(shè)置的內(nèi)存限制了。
例如,假設(shè)你在一個節(jié)點上安裝運行ZooKeeper、CLDB、MFS、JobTracker、TaskTracker、NFS、the GUI、HBase Master 和HBase RegionServer。這么多的服務(wù)運行在一個節(jié)點上,而且每個服務(wù)都需要內(nèi)存,所以warden會將內(nèi)存按照百分比分配給每個服務(wù),剩下的將會分配 給節(jié)點上的map/reduce 實例。如果你分配給這些服務(wù)總共60%還有5%為系統(tǒng)預(yù)留,那么就還有35%分給節(jié)點上的map/reduce實例。如果這個節(jié)點有10G的內(nèi)存,將會有3.5G分給 map/reduce 任務(wù)。如果你有 6個map slot和4個reduce slot。如果內(nèi)存是平均分配的,最終每個JVM的內(nèi)存限制為350MB。如果你需要512MB內(nèi)存來運行你的map任務(wù),那么默認設(shè)置的情況下是不會運行的,你會遇到Java堆空間錯誤。
當管理實例內(nèi)存的時候會意識到還有其它問題。不要強制節(jié)點去使用大量的交換空間(swap space)或者觸發(fā)頻繁內(nèi)存分頁讀寫磁盤。如果你通過顯式的在mapred.map.child.java.opts設(shè)置“-Xmx500m”來改變提交的作業(yè),將會重寫安全的內(nèi)存限制。但實際上你并沒有額外的物理內(nèi)存。雖然 map/reduce 實例仍能啟動,但是會強制使用大量的交換空間,而且無法依賴內(nèi)核的OOM killer或者其他的方法來防止這種情況發(fā)生。如果真的發(fā)生這種情況,無法指望節(jié)點啟動大量分頁來迅速恢復(fù)。如果只是增加了實例的JVM內(nèi)存,同時繼續(xù)在節(jié)點上啟動相同數(shù)量的實例。你會申請更多的內(nèi)存,需要注意不要超額申請。如果超額申請?zhí)嗟脑?,會?dǎo)致大量的分頁,這樣節(jié)點可能會被掛 起再也無法恢復(fù)。除非重啟電源。
所以如果你給每個實例JVM增加內(nèi)存的話,需要通過TaskTrackers來減少分配給map/reduce task slot數(shù)量。
這是一個很復(fù)雜的情況,因為如果你在集群上并發(fā)執(zhí)行不同的作業(yè),可能來自一個作業(yè)(JobA)的實例需要大量的內(nèi)存,來自另外一個作業(yè)(JobB)的實例只需要很少的內(nèi)存。因此,如果你減少map/reduce slot的數(shù)量,會發(fā)現(xiàn)會有足夠的內(nèi)存來運行來自JobB任務(wù)(task)。但是卻沒有足夠的內(nèi)存提供給JobA。所以關(guān)鍵就是找到一個平衡點,一個可以允許進行一些超額申請卻不會導(dǎo)致節(jié)點被掛起的平衡點。
為了協(xié)助這個任務(wù),TaskTracker 將會著眼于當前所有在運行的 map/reduce tasks 所使用的內(nèi)存數(shù)量。不是只看這些任務(wù)的最大內(nèi)存限制,而是所有運行中的實例實際利用的內(nèi)存總數(shù)。當消耗的內(nèi)存達到一定級別,TaskTracker 會殺死一些運行的實例來釋放內(nèi)存,以便其他的實例能正常執(zhí)行完并且不會造成節(jié)點上的分頁過多。
舉個例子,如果你想在一個小型的集群或者單一節(jié)點上運行wordcount示例,碰到“Java堆空間”錯誤,最簡單最快的解決方法就是通過編輯/opt/mapr/hadoop/hadoop-0.20.2/conf/mapred-site.xml中的設(shè)置來減少 map/reduce 實例 slot的數(shù)量:
- mapred.tasktracker.map.tasks.maximum
- mapred.tasktracker.reduce.tasks.maximum
將實例的slot的數(shù)量設(shè)置為小于當前計算結(jié)果是非常重要的。當前計算的數(shù)量可以通過進入JobTracker web界面來確定。例如,如果你有一個TaskTracker ,顯示它有6個mpa slot和4個 reduce slot,那么你應(yīng)該設(shè)置 3個map slot、2個 reduce slot。然后通過下面這行命令重啟節(jié)點上的TaskTracker進程:
- maprcli node services -nodes -tasktracker restart
減少slot的數(shù)量重新啟動后,重新提交wordcount作業(yè)。如果沒有額外內(nèi)存申請,每個實例、JVM都會分配到更多的內(nèi)存。這是一個安全的解決方法,節(jié)點不會產(chǎn)生大量分頁。這是一種簡單的解決方案,不需要大量計算內(nèi)存。這也是快速的方法,只需要編輯下配置文件并重啟下服務(wù)就好了。
為了避免Java堆空間錯誤,記住下面這些步驟:
- 估算你的實例需要消耗多少內(nèi)存。
- 確保TaskTracker 啟動你的實例時,JVM內(nèi)存的限制要大于等于你預(yù)計的內(nèi)存需求。
- 記住,啟動這些JVM是有默認設(shè)置的,除非你顯式的重寫過這些設(shè)置。在CPU核心數(shù)和物理內(nèi)存已經(jīng)平衡并運行服務(wù)的節(jié)點上,默認設(shè)置并不適用。
- 不要迫使節(jié)點大量的使用交換空間或者頻繁的將內(nèi)存分頁讀寫到磁盤上。
- 將實例slot數(shù)量設(shè)置為小于JobTracker web GUI計算值。
原文鏈接: mapr 翻譯: ImportNew.com - 光光頭去打醬油
譯文鏈接: http://www.importnew.com/14049.html