OutOfMemoryError系列(8): Kill process or sacrifice child

一言不合就殺進程。。。

這是本系列的第八篇文章, 相關文章列表:

爲了理解這個錯誤,我們先回顧一下操作系統相關的基礎知識。

我們知道, 操作系統(operating system)構建在進程(process)的基礎上. 進程由內核作業(kernel jobs)進行調度和維護, 其中有一個內核作業稱爲 “Out of memory killer(OOM終結者)”, 與本節所講的 OutOfMemoryError 有關。

Out of memory killer 在可用內存極低的情況下會殺死某些進程。只要達到觸發條件就會激活, 選中某個進程並殺掉。 通常採用啓發式算法, 對所有進程計算評分(heuristics scoring), 得分最低的進程將被 kill 掉。因此 Out of memory: Kill process or sacrifice child 和前面所講的 OutOfMemoryError 都不同, 因爲它既不由JVM觸發,也不由JVM代理, 而是系統內核內置的一種安全保護措施。

out of memory linux kernel

如果可用內存(含swap)不足, 就有可能會影響系統穩定, 這時候 Out of memory killer 就會設法找出流氓進程並殺死他, 也就是引起 Out of memory: kill process or sacrifice child 錯誤。

原因分析

默認情況下, Linux kernels(內核)允許進程申請的量超過系統可用內存. 這是因爲,在大多數情況下, 很多進程申請了很多內存, 但實際使用的量並沒有那麼多.
有個簡單的類比, 寬帶租賃的服務商, 可能他的總帶寬只有 10Gbps, 但卻賣出遠遠超過100份以上的 100Mbps 帶寬, 原因是多數時候, 寬帶用戶之間是錯峯的, 而且不可能每個用戶都用滿服務商所承諾的帶寬。

這樣的話,可能會有一個問題, 假若某些程序佔用了大量的系統內存, 那麼可用內存量就會極小, 導致沒有內存頁面(pages)可以分配給需要的進程。可能這時候會出現極端情況, 就是 root 用戶也不能通過 kill 來殺掉流氓進程. 爲了防止發生這種情況, 系統會自動激活 killer, 查找流氓進程並將其殺死。

更多關於 ”Out of memory killer“ 的性能調優細節, 請參考: RedHat 官方文檔.

現在我們知道了爲什麼會發生這種問題, 那爲什麼是半夜5點鐘觸發 “killer” 發報警信息給你呢? 通常觸發的原因在於操作系統配置. 例如, /proc/sys/vm/overcommit_memory 配置文件的值, 指定了是否允許所有的 malloc() 調用成功. 請注意, 在各操作系統中, 這個配置對應的 proc 文件路徑可能不同。

過量使用(overcommitting)配置, 允許流氓進程申請越來越多的內存, 最終惹得 ”Out of memory killer“ 出來搞事情。

示例

在Linux上(如最新穩定版的Ubuntu)編譯並執行以下的示例代碼:

package eu.plumbr.demo;

public class OOM {

public static void main(String[] args){
  java.util.List<int[]> l = new java.util.ArrayList();
  for (int i = 10000; i < 100000; i++) {
      try {
        l.add(new int[100_000_000]);
      } catch (Throwable t) {
        t.printStackTrace();
      }
    }
  }
}

將會在系統日誌中(如 /var/log/kern.log 文件)看到一個錯誤, 類似這樣:

Jun  4 07:41:59 plumbr kernel: 
    [70667120.897649]
    Out of memory: Kill process 29957 (java) score 366 or sacrifice child
Jun  4 07:41:59 plumbr kernel: 
    [70667120.897701]
    Killed process 29957 (java) total-vm:2532680kB, anon-rss:1416508kB, file-rss:0kB

提示: 可能需要調整 swap 的大小並設置最大堆內存, 例如堆內存配置爲 -Xmx2g, swap 配置如下:

swapoff -a 
dd if=/dev/zero of=swapfile bs=1024 count=655360
mkswap swapfile
swapon swapfile

解決方案

有多種處理辦法。最簡單的辦法就是將系統遷移到內存更大的實例中。

另外, 還可以通過 OOM killer 調優, 或者做負載均衡(水平擴展,集羣), 或者降低應用對內存的需求。

不太推薦的方案是加大交換空間/虛擬內存(swap space)。 試想一下, Java 包含了自動垃圾回收機制, 增加交換內存的代價會很高昂. 現代GC算法在處理物理內存時性能飛快, 但對交換內存來說,其效率就是硬傷了. 交換內存可能導致GC暫停的時間增長几個數量級, 因此在採用這個方案之前, 看看是否真的有這個必要。

原文鏈接: https://plumbr.eu/outofmemoryerror/kill-process-or-sacrifice-child

翻譯日期: 2017年9月21日

翻譯人員: 鐵錨: http://blog.csdn.net/renfufei

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章