
因為證明了沒有副作用,所以現(xiàn)在是沒啥問題了。但是我個人認為其實還有一種辦法是,取一個閾值,如果小于某個閾值則做double duplicate check,否則就直接加進去就好了。Spark 在很多地方也是這么做的。
這里對于那些Task數(shù)特別大的朋友有福了。
TimSort issue due to integer overflow for large buffer
該Bug在1.6.2, 2.0.0 已經被解決。這個bug引起的問題現(xiàn)象初看起來會比較讓人費解,大體如下:

Snip20160906_21.png
如圖所示似乎違反了簽名。其實問題本身確實比較復雜,通過提交了兩個patch 才解決了該問題。
一開始Facebook的哥們覺得應該是排序過程中內存的數(shù)據(jù)(比如ShuffleExternalSorter等Sorter) 超過8G 引起的,所以限制了數(shù)量,大于一定數(shù)量之后就進行spill操作。 后面一個新的PR應該是發(fā)現(xiàn)了問題的根源,在UnsafeSortDataFormat.copyRange() 和ShuffleSortDataFormat copyRange() 里,里面數(shù)組的偏移量是Integer類型,雖然數(shù)據(jù)集的大小不至于超過Int的最大值,但是在特定數(shù)據(jù)分布下且數(shù)據(jù)集>268.43 million 并則會觸發(fā)這個Bug。我看了下,原先 Platform.copyMemory 簽名本身也是Long的,但是實現(xiàn)copyRange的時候,默認傳進去的是Int,所以產生了這個問題。大家瞅一眼代碼就知道了。

Snip20160906_23.png
Fix Spark executor OOM
該Bug 也是在1.6.2, 2.0.0 被修正。
這個問題是這樣的,Spark MemoryManager 可能認為還有10M內存,但是此時實際JVM可以提供給MemroyManager的內存只有5M了。所以分配內存的時候,就拋OOM了。這個時候應該捕獲該OOM,并且保留已經申請到內存不歸還,讓MemoryManger 以為內存不夠了,然后進行splill操作,從而湊足需要的內存。我們看TaskMemoryManager.allocatePage 方法。

Snip20160906_25.png
如果發(fā)生OOM了,則會捕獲一次,,并且通過acquiredButNotUsed記住已經申請的量,最后再次調用allocatePage。這個時候allocatePage里的acquireExecutionMemory 方法可能發(fā)現(xiàn)自己內存不足了,就會發(fā)生spill了,從而釋放出內存。
其實這之前的代碼也考慮過,但是沒有在allocatePage的層次上做。這個Bug估計在單個Executor 并行運行Task數(shù)比較多的時候比較嚴重和容易發(fā)生的。
Fix memory leak in the sorter SPARK-14363
這個Bug 也是在1.6.2, 2.0.0被修正。
在Spark排序中,指針和數(shù)據(jù)時分開存儲的,進行spill操作其實是把數(shù)據(jù)替換到磁盤上。但是指針數(shù)組是必須在內存里。當數(shù)據(jù)被spill后,相應的,指向這些記錄的指針其實也是要被釋放的。數(shù)據(jù)量很大的時候,指針數(shù)組的大小也很可觀。而且有一點值得指出的是,比如某個Executor 有五個Task并行運行,如果其中有三個完成了,那么可用內存增大,緩存到內存的數(shù)據(jù)就會變多,這個時候剩下的兩個Task的指針數(shù)組也會增大,從而占用更多內存,接著新運行的三個Task可用內存變小了,從而失去了公平性。
這些各個Sorter里都需要修正。

Snip20160906_26.png
紅框部分便是釋放指針數(shù)組的地方。里面會重新按初始initialSize值申請一塊指針數(shù)組的內存。