jmap


java.lang.OutOfMemoryError: Java heap space

當應用程序申請更多的內存,而Java堆內存已經無法滿足應用程序對內存的需要,就會拋出此種異常。

java.lang.OutOfMemoryError: PermGen space及其解決方法

表示Java永久帶(方法區)空間不夠,永久帶用於存放類的字節碼和長常量池,類的字節碼加載后存放在這個區域,這和存放對象實例的堆區是不同的,大多數JVM的實現都不會對永久帶進行垃圾回收,因此,只要類加載的過多就會出現這個問題。一般的應用程序都不會產生這個錯誤,然而,對於Web服務器來講,會產生有大量的JSP,JSP在運行時被動態的編譯成Java Servlet類,然后加載到方法區,因此,太多的JSP的Web工程可能產生這個異常。

java.lang.OutOfMemoryError: unable to create new native thread

創建了太多的線程,而能創建的線程數是有限制的,導致了這種異常的發生。

在Linux下線程使用輕量級進程實現的,因此線程的最大數量也是操作系統允許的進程的最大數量。

java.lang.OutOfMemoryError:GC overhead limit exceeded

是在並行或者並發回收器在GC回收時間過長、超過98%的時間用來做GC並且回收了不到2%的堆內存,然后拋出這種異常進行提前預警,用來避免內存過小造成應用不能正常工作。

java.lang.StackOverflowError ...

JVM的線程由於遞歸或者方法調用層次太多,占滿了線程堆棧而導致的,線程堆棧默認大小為1M。

java.net.SocketException: Too many open files

由於系統對文件句柄的使用是有限制的,而某個應用程序使用的文件句柄超過了這個限制,就會導致這個問題。

 

工具使用:

jmap命令(Java Memory Map)

map命令可以獲得運行中的jvm的堆的快照,從而可以離線分析堆,以檢查內存泄漏,檢查一些嚴重影響性能的大對象的創建,檢查系統中什么對象最多,各種對象所占內存的大小等等。可以使用jmap生成Heap Dump。 

JVM Memory Map命令用於生成heap dump文件,如果不使用這個命令,還可以使用-XX:+HeapDumpOnOutOfMemoryError參數來讓虛擬機出現OOM的時候自動生成dump文件。 jmap不僅能生成dump文件,還可以查詢finalize執行隊列、Java堆和永久代的詳細信息,如當前使用率、當前使用的是哪種收集器等。

 

 

常用命令:

-dump

dump堆到文件,format指定輸出格式,live指明是活着的對象,file指定文件名,:live是可選的。

[root@localhost jdk1.7.0_79] # jmap -dump:live,format=b,file=dump.hprof 24971
Dumping heap to /usr/local/java/jdk1 .7.0_79 /dump .hprof ...
Heap dump file created

可以將24971進程的內存heap以二進制形式(format=b)輸出出來到dump.hprof文件里,再配合MAT(內存分析工具)。

-heap

打印heap的概要信息,GC使用的算法,heap的配置及使用情況,可以用此來判斷內存目前的使用情況以及垃圾回收情況
 
 
 
 
[ci@localhost apps]$ jmap -heap 8633
Attaching to process ID 8633, please wait...   
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b17

using parallel threads in the new generation.  ##新生代采用的是並行線程處理方式
using thread-local object allocation.
Concurrent Mark-Sweep GC  ##同步並行垃圾回收

Heap Configuration: ##堆配置情況
   MinHeapFreeRatio         = 40  ##最小堆使用比例 (GC后如果發現空閑堆內存占到整個預估堆內存的N%(百分比), 則放大堆內存的預估最大值)
   MaxHeapFreeRatio         = 70  ##最大堆可用比例 (GC后如果發現空閑堆內存占到整個預估堆內存的N%(百分比),則收縮堆內存的預估最大值, 預估堆內存是堆大小動態調控的重要選項之一. 堆內存預估最大值一定小於或等於固定最大值(-Xmx指定的數值). 前者會根據使用情況動態調大或縮小, 以提高GC回收的效率)
   MaxHeapSize              = 1073741824 (1024.0MB)  ##最大堆空間大小 (即-Xmx, 堆內存大小的上限)
   NewSize                  = 89456640 (85.3125MB)  ##新生代分配大小(新生代預估堆內存占用的默認值)
   MaxNewSize               = 348913664 (332.75MB)  ##最大可新生代分配大小(新生代占整個堆內存的最大值)
   OldSize                  = 178978816 (170.6875MB) ##老年代大小(老年代的默認大小, default size of the tenured generation)
   NewRatio                 = 2  ##新生代比例(老年代對比新生代的空間大小, 比如2代表老年代空間是新生代的兩倍大小. The ratio of old generation to young generation.)
   SurvivorRatio            = 8  ##新生代與suvivor的比例(Eden/Survivor的值. 這個值的說明, 很多網上轉載的都是錯的. 8表示Survivor:Eden=1:8, 因為survivor區有2個, 所以Eden的占比為8/10. Ratio of eden/survivor space size. -XX:SurvivorRatio=6 sets the ratio between each survivor space and eden to be 1:6, each survivor space will be one eighth of the young generation.

   MetaspaceSize            = 21807104 (20.796875MB)  ##JDK1.8的方法區大小(分配給類元數據空間的初始大小(Oracle邏輯存儲上的初始高水位,the initial high-water-mark ). 此值為估計值. MetaspaceSize設置得過大會延長垃圾回收時間. 垃圾回收過后, 引起下一次垃圾回收的類元數據空間的大小可能會變大)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)(類指針壓縮空間大小, 默認為1G)
   MaxMetaspaceSize         = 17592186044415 MB   ##JDK1.8的方法區最大(是分配給類元數據空間的最大值, 超過此值就會觸發Full GC. 此值僅受限於系統內存的大小, JVM會動態地改變此值)
   G1HeapRegionSize         = 0 (0.0MB)(G1區塊的大小, 取值為1M至32M. 其取值是要根據最小Heap大小划分出2048個區塊. With G1 the Java heap is subdivided into uniformly sized regions. This sets the size of the individual sub-divisions. The default value of this parameter is determined ergonomically based upon heap size. The minimum value is 1Mb and the maximum value is 32Mb. Sets the size of a G1 region. The value will be a power of two and can range from 1MB to 32MB. The goal is to have around 2048 regions based on the minimum Java heap size.)

Heap Usage:
New Generation (Eden + 1 Survivor Space):  ##新生代(伊甸區 + survior空間)
   capacity = 80543744 (76.8125MB)
   used     = 34725560 (33.11687469482422MB)  ##已經使用大小
   free     = 45818184 (43.69562530517578MB)  ##剩余容量
   43.11391335371745% used
Eden Space:   
   capacity = 71630848 (68.3125MB)
   used     = 29683880 (28.308753967285156MB)
   free     = 41946968 (40.003746032714844MB)
   41.44007900060041% used
From Space:
   capacity = 8912896 (8.5MB)
   used     = 5041680 (4.8081207275390625MB)
   free     = 3871216 (3.6918792724609375MB)
   56.56612620634191% used
To Space:
   capacity = 8912896 (8.5MB)
   used     = 0 (0.0MB)
   free     = 8912896 (8.5MB)
   0.0% used
concurrent mark-sweep generation:  ##老年代使用情況
   capacity = 178978816 (170.6875MB)
   used     = 134752632 (128.51012420654297MB)
   free     = 44226184 (42.17737579345703MB)
   75.28971026381133% used

36267 interned Strings occupying 3977048 bytes.
 
 
-finalizerinfo 
打印正等候回收的對象的信息
jmap -finalizerinfo pid

 

 
jmap -histo[:live] pid
 打印每個class的實例數目,內存占用,類全名信息. VM的內部類名字開頭會加上前綴”*”. 如果live子參數加上后,只統計活的對象數量.

 
 
 class name是對象類型,說明如下:
byte
char
double
float
int
long
boolean
[  數組,如[I表示 int []
[L+類名 其他對象
 
 
 
 
 
 
 
 
 
 
 
 
 

如果還需要看更加詳細的信息,則使用:

jmap -dump:format=b,file=dumpFileName pid  直接生成在執行命令時所在的目錄下面。

dump出來的文件可以用MAT、VisualVM等工具查看,也可以使用jhat。

jhat -port 9999 dumpFileName

如果dump出來的文件過大,可能需要指定Xmx(jhat實際啟動了一個web應用)。

jhat -J-Xmx1000m -port 9999 dumpFileName

啟動成功后,則可以通過瀏覽器查看:

ip:port

 
 
 
 
 

 

 

 

 

 

例如上圖查詢的是長度大於256的int數組。

 

jmap -histo:live 24971 | grep com.yuhuo 查詢類名包含com.yuhuo的信息

jmap -histo:live 24971 | grep com.yuhuo > histo.txt 保存信息到histo.txt文件

 

-F 強迫.在pid沒有響應的時候使用-dump或者-histo參數. 在這個模式下,live子參數無效.
-h | -help 打印輔助信息
-J<flag> 傳遞參數給jmap啟動的jvm.
pid 需要被打印配相信息的java進程id,可以用jps查問

 

 

從安全點日志看,從Heap Dump開始,整個JVM都是停頓的,考慮到IO(雖是寫到Page Cache,但或許會遇到background flush),幾G的Heap可能產生幾秒的停頓,在生產環境上執行時謹慎再謹慎。

 

live的選項,實際上是產生一次Full GC來保證只看還存活的對象。有時候也會故意不加live選項,看歷史對象。

 

Dump出來的文件建議用JDK自帶的VisualVM或Eclipse的MAT插件打開,對象的大小有兩種統計方式:

 

  • 本身大小(Shallow Size):對象本來的大小。
  • 保留大小(Retained Size): 當前對象大小 + 當前對象直接或間接引用到的對象的大小總和。

 

看本身大小時,占大頭的都是char[] ,byte[]之類的,沒什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要關心的是保留大小比較大的對象,看誰在引用這些char[], byte[]。

 

(MAT能看的信息更多,但VisualVM勝在JVM自帶,用法如下:命令行輸入jvisualvm,文件->裝入->堆Dump->檢查 -> 查找20保留大小最大的對象,就會觸發保留大小的計算,然后就可以類視圖里瀏覽,按保留大小排序了)

 

 

 

 

 

轉自:http://www.cnblogs.com/gaojk/articles/3886503.html

 

1、發現問題

 

1)、使用uptime命令查看CPU的Load情況,Load越高說明問題越嚴重;

 

2)、使用jstat查看FGC發生的頻率及FGC所花費的時間,FGC發生的頻率越快、花費的時間越高,問題越嚴重;

 

 

 

2、導出數據:在應用快要發生FGC的時候把堆導出來

 

1)、查看快要發生FGC使用命令:

 

jmap -heap <pid>

 

會看到如下圖結果:

 

 

    以上截圖包括了新生代、老年代及持久代的當前使用情況,如果不停的重復上面的命令,會看到這些數字的變化,變化越大說明系統存在問題的可能性越大,特別是被紅色圈起來的老年代的變化情況。現在看到的這個值為使用率為99%或才快接近的時候,就立即可以執行導出堆棧的操作了。
    注:這是因為我這里沒有在jvm參數中使用"-server"參數,也沒有指定FGC的閥值,在線上的應用中通過會指定CMSInitiatingOccupancyFraction這個參數來指定當老年代使用了百分之多少的時候,通過CMS進行FGC,當然這個參數需要和這些參數一起使用“-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly”,CMSInitiatingOccupancyFraction的默認值是68,現在中文站線上的應用都是70,也就是說當老年代使用率真達到或者超過70%時,就會進行FGC。
    2)、將數據導出:
jmap -dump:format=b,file=heap.bin <pid>
這個時候會在當前目錄以生成一個heap.bin這個二進制文件。
 
3、通過命令查看大對象
也是使用jmap的命令,只不過參數使用-histo
使用:jmap -histo <pid>|less
可得到如下包含對象序號、某個對象示例數、當前對象所占內存的大小、當前對象的全限定名,如下圖:
查看對象數最多的對象,並按降序排序輸出:

 

執行:jmap -histo <pid>|grep alibaba|sort -k 2 -g -r|less

 

結果如圖:

 

 

查看占用內存最多的最象,並按降序排序輸出:

 

執行:jmap -histo <pid>|grep alibaba|sort -k 3 -g -r|less

 

結果如圖:

 

 


4、數據分析
這個時候將dump出的文件在ECLIPSE中打開,使用MAT進行分析(ECLIPSE需要先安裝MAT插件),會展示如下截圖:

 

可以從這個圖看出這個類java.lang.ref.Finalizer占用500多M,表示這其中很多不能夠被回對象的對象,此時點開hisgogram視圖,並通過Retained Heap進行排序,如下截圖:

 

 

從圖中可以看出,被線線框圈起來的三個對象占用量非常大,那說明這幾個大的對象並沒有被釋放,那現在就可以有針對性的從代碼中去找這幾個對象為什么沒有被釋放了。
再切換到dominator_tree視圖:

 

這里可以看到velocity渲染也存在着問題,以及數據庫的請求也比較多。

 

 

 

5、優化
優化的思路就是上面所列出來的問題,查看實現代碼中所存在問題,具體問題具體分析。
 

總結

1.如果程序內存不足或者頻繁GC,很有可能存在內存泄露情況,這時候就要借助Java堆Dump查看對象的情況。
2.要制作堆Dump可以直接使用jvm自帶的jmap命令
3.可以先使用jmap -heap命令查看堆的使用情況,看一下各個堆空間的占用情況。
4.使用jmap -histo:[live]查看堆內存中的對象的情況。如果有大量對象在持續被引用,並沒有被釋放掉,那就產生了內存泄露,就要結合代碼,把不用的對象釋放掉。
5.也可以使用 jmap -dump:format=b,file=<fileName>命令將堆信息保存到一個文件中,再借助jhat命令查看詳細內容
6.在內存出現泄露、溢出或者其它前提條件下,建議多dump幾次內存,把內存文件進行編號歸檔,便於后續內存整理分析。

7.在用cms gc的情況下,執行jmap -heap有些時候會導致進程變T,因此強烈建議別執行這個命令,如果想獲取內存目前每個區域的使用狀況,可通過jstat -gc或jstat -gccapacity來拿到。

 

 

Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can’t attach to the process

在ubuntu中第一次使用jmap會報錯:Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process,這是oracla文檔中提到的一個bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7050524,解決方式如下:

  1. echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 該方法在下次重啟前有效。

  2. 永久有效方法 sudo vi /etc/sysctl.d/10-ptrace.conf 編輯下面這行: kernel.yama.ptrace_scope = 1 修改為: kernel.yama.ptrace_scope = 0 重啟系統,使修改生效。

 

 

寫了一個OOM。

public class OOM {
public static void main(String[] args) {
List<StateMachine> list = new ArrayList<StateMachine>();
while(true){
list.add(new StateMachine());
}
}
}
使用如下配置:
-server
-Xms1024m
-Xmx1024m
-Xmn384m
-XX:+UseParallelOldGC
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/yp-tc-m-2777/Desktop/heap.bin

 

 


免責聲明!

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



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