Java 性能優化實戰記錄(3)--JVM OOM的分析和原因追查


 

前言:
  C/C++的程序員渴望Java的自由, Java程序員期許C/C++的約束. 其實那里都是圍城, 外面的人想進來, 里面的人想出去.

背景:
  作為Java程序員, 除了享受垃圾回收機制帶來的便利外, 還深受OOM(Out Of Memory)的困惑和折磨. 本文借鑒了<<深入理解 Java虛擬機>>, 並結合了小編自身的經歷和讀者一起面對OOM的困局如何分析和破解.

准備工作:
  工欲善其事必先利其器, 對java進程的快照分析, 是能夠幫助我們迅速的定位出錯的原因. 這邊我們着重介紹內存分析相關的點.
  借助MAT(Memory Analyzer Tool)來分析內存快照.
  MAT的下載地址:
  http://www.eclipse.org/mat/downloads.php
  eclipse的插件url:
  http://download.eclipse.org/mat/1.4/update-site/
  MAT的展示效果如圖所示:

內存快照:
  如何去觸發java進程生成內存快照呢?
  1). 設定jvm參數, 使得進程在特定條件下進程內存dump.

-XX:+HeapDumpOnOutOfMemoryError	

  評注: 設置如下虛擬機參數后, 當java進程因為內存溢出, 就會自動對內存進行Dump, 以便相關人員事后進行分析/解讀.
  該方案非常具有針對性, 在崩潰點分析往往能立馬定位問題的所在. 該參數設定方案相當於飛機的黑盒子, 記錄了出事前所有信息.
  當然該方案, 最大的局限性就是不能隨時隨地進行分析, 那如何破呢?
  2). JMap工具來生成
  JMap的使用命令描述:

Usage:
  jmap [option] <pid>
    (to connect to running process)
  jmap [option] <executable <core>
    (to connect to a core file)
  jmap [option] [server_id@]<remote server IP or hostname>
    (to connect to remote debug server)

where <option> is one of:
  -dump:<dump-options> to dump java heap in hprof binary format
    dump-options:
    live	dump only live objects; if not specified,
          all objects in the heap are dumped.
    format=b binary format
    file=<file> dump heap to <file>
  Example: jmap -dump:live,format=b,file=heap.bin <pid>

  評注: 挑選了部分選項說明, 重要的是dump的規則
  使用命令如下所示:

jmap -dump:format=b,file=xxx.hprof <pid> 

OOM的原因分類:
  導致程序出現OOM的因素有多種. 大致我們可以把OOM簡單的分為堆溢出(Heap), 棧溢出(Stack), 永久代溢出(常量池/方法區), 直接內存溢出.
  先來看下java的內存分布
  1). 堆溢出(heap)
  編寫如下例程:

public static void main(String[] args) {
  List<byte[]> datas = new ArrayList<byte[]>();
  while ( true ) {
    datas.add(new byte[1024 * 1024]);
  }
}

  同時設置虛擬機參數, 使得java進程能夠快速生成heapdump.

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

  評注: 設置heap的內存限制在20m, Xms/Xmx分別對應heap的初始和最大heap大小.
  產生的異常信息:

  mat進行內存分析

  評注: 從這邊能看出, 這邊聚集了眾多的對象, 占據了99%的內存量.
  2). 棧溢出(stack)
  棧溢出的異常, 還是具有明確的指向性的. 暫略.
  3). 永久代溢出(常量池/方法區)
  <<深入理解 Java虛擬機>>闡述了String.intern()和gclib產生動態代理類的兩個例子.
  其錯誤也很有指向性:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

  這邊講述下, 小編(mumuxinfei)很久之前做的一個項目.
  當時是機頂盒項目(采用IPanel瀏覽器, 相當於jvm虛擬機), 采用J2ME規范(java 1.1). 不過當時的機器性能不好, 內存少, 不像現在, 一些移動設備配置開始像PC靠齊了.
  我把一些初始化數據放在類里聲明並定義時, 編譯沒問題, 但是jvm無論如何也run不起來, 總是出錯. 不過我把這些數據放在文件里時, 通過運行時載入, 這樣就沒問題. 當時的我特別疑惑, 不都是內存嗎? 同樣的數據, 同樣的內存, 這么差別就這么大呢?
  當時的我不理解, 后來知道java的內存結構, 我就明白了. 載入的數據放在Heap中, 而定義的常量是放在PermGen空間中. 而當初的設備限制, IPanel瀏覽器的默認永久空間大小比較小, 導致我只要把一些數據寫到常量中定義, 就運行失敗.
  不過蠻荒時代, 總是過去了, 現在的機器配置, 你是不會遇到這么詭異的事了. 日子在一天一天的變好, 過去苦難的日子, 現在的小年輕, 你是不會懂的...
  4). 直接內存溢出
  <<深入理解 Java虛擬機>>講述了DriectByteBuffer的例子. 當然最直接, 也是最容易遇到的, 還是JNI的使用, 搞Android開發的大拿故意能遇到. 這邊也略過不講.

后記:
  從某種角度來說, <<深入理解 Java虛擬機>>已經把OOM的原因和例子說得非常具體了, 小編難以去超越它, 也不願簡單的復制羅列抄襲之. 簡單的描述, 權當作學習筆記了.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM