Troubleshooting 整理
- kill -3 pid
- 發(fā)送一個SIGQUIT信號給Java應用, 通常會有當前的Thread Dump輸出
- 假定這個程序在JVM初始化之后沒有別的代碼注冊了新的SIGQUIT的signal handler,那么HotSpot VM在收到SIGQUIT之后會在一個專門的signal handler thread處理蚕甥。該線程的入口函數為signal_thread_entry()
- http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/tip/src/share/vm/runtime/os.cpp
- 打印線程棧的動作由VM_PrintThreads實現(xiàn)惧磺,在VM thread上執(zhí)行。于是問題就來了:如果這個JVM實例已經hang了挺据,那它將無法響應任何外部請求取具,對它發(fā)SIGQUIT當然也得不到處理。所以有時候kill -3看不到線程棧是正常的扁耐。
- 至于時效性暇检,所有VM operation都是先被放到一個隊列里,然后由VM thread逐個處理婉称。如果當前該隊列是空的块仆,那kill -3就可以幾乎“實時”執(zhí)行爬棧動作,否則得等前面的VM operation先完成王暗,那就會延遲一會兒悔据。
- Linux core dump
- concept
- A core dump is the recorded state of the working memory of a computer program at a specific time, generally when the program has terminated abnormally (crashed). In practice, other key pieces of program state are usually dumped at the same time, including the processor registers, which may include the program counter and stack pointer, memory management information, and other processor and operating system flags and information. The name comes from the once-standard memory technology core memory. Core dumps are often used to diagnose or debug errors in computer programs.
- On many operating systems, a fatal error in a program automatically triggers a core dump, and by extension the phrase "to dump core" has come to mean, in many cases, any fatal error, regardless of whether a record of the program memory is created.
- 檢查是否可以core dump
- ulimit -a (or unlimit -c只查看core file size)
core file size (blocks, -c) 0
... - 修改限制
- 臨時 可以使用參數unlimited,取消該限制ulimit -c unlimited
- 永久 echo "ulimit -c 1024" >> /etc/profile (1024 限制產生的 core 文件的大小不能超過 1024kb)
- ulimit -a (or unlimit -c只查看core file size)
- 在一個程序崩潰時俗壹,它一般會在指定目錄下生成一個 core 文件科汗。 core 文件僅僅是一個內存映象 ( 同時加上調試信息 ) ,主要是用來調試的绷雏。
- 設置 Core Dump 的核心轉儲文件目錄和命名規(guī)則
- /proc/sys/kernel/core_uses_pid 可以控制產生的 core 文件的文件名中是否添加 pid 作為擴展 头滔,如果添加則文件內容為 1 ,否則為 0
- /proc/sys/kernel/core_pattern 可以設置格式化的 core 文件保存位置或文件名 涎显,比如原來文件內容是 core-%e 可以這樣修改 : echo "/corefile/core-%e-%p-%t" > core_pattern; 將會控制所產生的 core 文件會存放到 /corefile 目錄下坤检,產生的文件名為 core- 命令名 -pid- 時間戳
- kill -l 查看信號
- 看到SIGSEGV在其中,一般數組越界或是訪問空指針都會產生這個信號
- 指示進程進行了一次無效的存儲訪問期吓。名字SEGV表示“段違例(segmentation violation)
- gdb core調試
- HSDB R大-HSDB
- concept
- JVM調優(yōu)-標準參數-的一些陷阱
- 各參數的默認值
- 1.參考HotSpot VM里的各個globals.hpp文件 本帖子列出一些
- 2.-XX:+PrintCommandLineFlags
- 顯示出VM初始化完畢后所有跟最初的默認值不同的參數及它們的值
- 3.-XX:+PrintFlagsFinal (這個參數本身只從JDK 6 update 21開始才可以用)
- 前一個參數只顯示跟默認值不同的早歇,而這個參數則可以顯示所有可設置的參數及它們的值
- 可以設置的參數默認是不包括diagnostic或experimental系的。要在-XX:+PrintFlagsFinal的輸出里看到這兩種參數的信息讨勤,分別需要顯式指定-XX:+UnlockDiagnosticVMOptions / -XX:+UnlockExperimentalVMOptions
- 4,-XX:+PrintFlagsInitial
- 這個參數顯示在處理參數之前所有可設置的參數及它們的值箭跳,然后直接退出程序⌒螅“參數處理”包括許多步驟衅码,例如說檢查參數之間是否有沖突,通過ergonomics調整某些參數的值脊岳,之類的
- 結合-XX:+PrintFlagsInitial與-XX:+PrintFlagsFinal逝段,對比兩者的差異,就可以知道ergonomics對哪些參數做了怎樣的調整
- $ java -XX:+PrintFlagsInitial | grep UseCompressedOops
bool UseCompressedOops = false {lp64_product}
$ java -XX:+PrintFlagsFinal | grep UseCompressedOops
bool UseCompressedOops = true {lp64_product}
Oracle JDK從6 update 23開始在64位系統(tǒng)上會默認開啟壓縮指針
- 5.jinfo -flag 可以用來查看某個參數的值割捅,也可以用來設定manageable系參數的值
- PrintFlagsInitial->ergonomics->命令指定->PrintFlagsFinal->jinfo
- 各參數的默認值
- 陷阱1
- -XX:+DisableExplicitGC 與 NIO的direct memory
- System.gc()的默認效果是引發(fā)一次stop-the-world的full GC奶躯,對整個GC堆做收集。有幾個參數可以改變默認行為
- 關鍵點是亿驾,用了-XX:+DisableExplicitGC參數后嘹黔,System.gc()的調用就會變成一個空調用,完全不會觸發(fā)任何GC,可以看看native源碼, Runtime.c里面;
- 為什么要關閉顯示調用呢?
- 避免濫用System.gc()
- 防止一些第三方庫濫調用System.gc()
- NIO的DirectByteBuffer里面有用到System.gc(),用來觸發(fā)回收DirectByteBuffer對象,從而間接回收堆外內存(reference handler里調用了Cleaner.cleanup() ),如果System.gc()被關閉了,導致DirectByteBuffer對象回收不了,最后直接內存也回收不了; java.lang.OutOfMemoryError: Direct buffer memory
public class DisableExplicitGCDemo { public static void main(String[] args) { for (int i = 0; i < 100000; i++) { ByteBuffer.allocateDirect(128); } System.out.println("Done"); } }
- java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
- DirectByteBuffer有幾處值得注意的地方
- DirectByteBuffer沒有finalizer儡蔓,它的native memory的清理工作是通過sun.misc.Cleaner自動完成的
- sun.misc.Cleaner是一種基于PhantomReference的清理工具郭蕉,比普通的finalizer輕量些
- A cleaner tracks a referent object and encapsulates a thunk of arbitrary cleanup code. Some time after the GC detects that a cleaner's referent has become phantom-reachable, the reference-handler thread will run the cleaner
- Oracle/Sun JDK 6中的HotSpot VM只會在old gen GC(full GC/major GC或者concurrent GC都算)的時候才會對old gen中的對象做reference processing,而在young GC/minor GC時只會對young gen里的對象做reference processing; 死在young gen中的DirectByteBuffer對象會在young GC時被處理; 也就是說喂江,做full GC的話會對old gen做reference processing召锈,進而能觸發(fā)Cleaner對已死的DirectByteBuffer對象做清理工作。而如果很長一段時間里沒做過GC或者只做了young GC的話則不會在old gen觸發(fā)Cleaner的工作获询,那么就可能讓本來已經死了的涨岁、但已經晉升到old gen的DirectByteBuffer關聯(lián)的native memory得不到及時釋放
- 為DirectByteBuffer分配空間過程中會顯式調用System.gc(),以期通過full GC來強迫已經無用的DirectByteBuffer對象釋放掉它們關聯(lián)的native memory
- 這幾個實現(xiàn)特征使得Oracle/Sun JDK 6依賴于System.gc()觸發(fā)GC來保證DirectByteMemory的清理工作能及時完成吉嚣。如果打開了-XX:+DisableExplicitGC梢薪,清理工作就可能得不到及時完成,于是就有機會見到direct memory的OOM
- 教訓是:如果你在使用Oracle/Sun JDK 6尝哆,應用里有任何地方用了direct memory秉撇,那么使用-XX:+DisableExplicitGC要小心。如果用了該參數而且遇到direct memory的OOM较解,可以嘗試去掉該參數看是否能避開這種OOM畜疾。如果擔心System.gc()調用造成full GC頻繁,可以嘗試下面提到 -XX:+ExplicitGCInvokesConcurrent 參數 結合笨神的堆外內存完全解讀
- -XX:+DisableExplicitGC 與 NIO的direct memory
- 陷阱2
- -XX:+DisableExplicitGC 與 Remote Method Invocation (RMI) 與 -Dsun.rmi.dgc.{server|client}.gcInterval=
- 實際上這里在做的是分布式GC印衔。Sun JDK的分布式GC是用純Java實現(xiàn)的,為RMI服務
- 前面gcCauses整理有整理這個dgc
- 陷阱3
- -XX:+ExplicitGCInvokesConcurrent 或 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
- product(bool, ExplicitGCInvokesConcurrent, false, "A System.gc() request invokes a concurrent collection;" " (effective only when UseConcMarkSweepGC)")
- product(bool, ExplicitGCInvokesConcurrentAndUnloadsClasses, false, "A System.gc() request invokes a concurrent collection and also unloads classes during such a concurrent gc cycle " "(effective only when UseConcMarkSweepGC)")
- 跟上面的第一個例子的-XX:+DisableExplicitGC一樣姥敛,這兩個參數也是用來改變System.gc()的默認行為用的奸焙;不同的是這兩個參數只能配合CMS使用(-XX:+UseConcMarkSweepGC),而且System.gc()還是會觸發(fā)GC的彤敛,只不過不是觸發(fā)一個完全stop-the-world的full GC与帆,而是一次并發(fā)GC周期
- CMS GC周期中也會做reference processing。所以如果用這兩個參數的其中一個墨榄,而不是用-XX:+DisableExplicitGC的話玄糟,就避開了由full GC帶來的長GC pause,同時NIO direct memory的OOM也不會那么容易發(fā)生
- 陷阱4
- -XX:+GCLockerInvokesConcurrent
- product(bool, GCLockerInvokesConcurrent, false, "The exit of a JNI CS necessitating a scavenge also kicks off a bkgrd concurrent collection")
- jni critical release時, 觸發(fā)gc; gcCauses整理有詳細
- 陷阱5
- MaxDirectMemorySize 與 NIO direct memory 的默認上限
- product(intx, MaxDirectMemorySize, -1, "Maximum total size of NIO direct-buffer allocations")
- 但如果不配置它的話袄秩,direct memory默認最多能申請多少內存呢阵翎?這個參數默認值是-1,顯然不是一個“有效值”之剧。所以真正的默認值肯定是從別的地方來的
- 當MaxDirectMemorySize參數沒被顯式設置時它的值就是-1郭卫,在Java類庫初始化時maxDirectMemory()被java.lang.System的靜態(tài)構造器調用,走的路徑就是這條背稼;
- 結論:MaxDirectMemorySize沒顯式配置的時候贰军,NIO direct memory可申請的空間的上限就是-Xmx減去一個survivor space的預留大小
- 這里建議讀讀源碼看看
- 陷阱6
- -verbose:gc 與 -XX:+PrintGCDetails
- 經常能看到在推薦的標準參數里這兩個參數一起出現(xiàn)。實際上它們有啥關系蟹肘?
在Oracle/Sun JDK 6里,"java"這個啟動程序遇到"-verbosegc"會將其轉換為"-verbose:gc",將啟動參數傳給HotSpot VM后红省,HotSpot VM遇到"-verbose:gc"則會當作"-XX:+PrintGC"來處理眯娱。
也就是說 -verbosegc、-verbose:gc晨仑、-XX:+PrintGC 三者的作用是完全一樣的。
而當HotSpot VM遇到 -XX:+PrintGCDetails 參數時,會順帶把 -XX:+PrintGC 給設置上米辐。
也就是說 -XX:+PrintGCDetails 包含 -XX:+PrintGC,進而也就包含 -verbose:gc书释。
既然 -verbose:gc 都被包含了翘贮,何必在命令行參數里顯式設置它呢?
- -XX:+UseFastEmptyMethods 與 -XX:+UseFastAccessorMethods
- 這個用的少, 只看下結論就行了
- 為了適應多層編譯模式爆惧,JDK 7里這兩個參數的默認值就被改為false了
- -XX:+UseCMSCompactAtFullCollection
- CMSFullGCsBeforeCompaction
- CMSParallelRemarkEnabled
- CMSScavengeBeforeRemark
- Attempt scavenge before the CMS remark step
- 如果一個應用統(tǒng)計到的young GC時間都比較短而CMS remark的時間比較長狸页,那么可以試試打開這個參數,在做remark之前先做一次young GC扯再。是否能有效縮短remark的時間視應用情況而異芍耘,所以開這個參數的話請一定做好測試
- -Xss 與 -XX:ThreadStackSize
- -Xmn 與 -XX:NewSize、-XX:MaxNewSize
- 如果同時設置了-XX:NewSize與-XX:MaxNewSize遇到“Could not reserve enough space for object heap”錯誤的話熄阻,請看看是不是這帖所說的問題斋竞。早期JDK 6似乎都受這問題影響,一直到JDK 6 update 14才修復
- -Xmn 與 -XX:NewRatio
- -XX:NewRatio 與 -XX:NewSize秃殉、-XX:OldSize
- jmap -heap看到的參數值與實際起作用的參數的關系坝初?
- jmap -heap顯示的部分參數是以MB為單位來顯示的,而MaxNewSize的單位是byte
- 要注意的是钾军,HotSpot VM有大量可調節(jié)的參數鳄袍,并不是所有參數在某次運行的時候都有效。
- -XX:MaxTenuringThreshold 的默認值吏恭?
- Oracle/Sun JDK 6中拗小,選擇CMS之外的GC時,MaxTenuringThreshold(以下簡稱MTT)的默認值是15樱哼;而選擇了CMS的時候哀九,MTT的默認值是4而不是15。設定是在 Arguments::set_cms_and_parnew_gc_flags() 里做的
- 在Sun JDK 6之前(1.4.2唇礁、5)勾栗,選擇CMS的時候MTT的默認值則是0,也就是等于設定了-XX:+AlwaysTenure——所有eden里的活對象在經歷第一次minor GC的時候就會直接晉升到old gen盏筐,而survivor space直接就沒用了
- java -XX:+PrintFlagsFinal | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' (15)
- java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | egrep 'MaxTenuringThreshold|UseParallelGC|UseConcMarkSweepGC' (本機是6)
- 建議這樣查看vm參數值:java 我們的vm配置參數 -XX:PrintFlagsFinal | grep ''selective parameter (原因在于有的參數的值,會受到其他參數影響導致與自己的默認值不一樣围俘!)
- -XX:+CMSClassUnloadingEnabled
- CMS remark暫停時間會增加,所以如果類加載并不頻繁、String的intern也沒有大量使用的話界牡,這個參數還是不開的好
- -XX:+UseCompressedOops 有益簿寂?有害?
- 我能做的建議是如果在64位Oracle/Sun JDK 6/7上宿亡,那個參數不要顯式設置
- 有些庫比較“聰明”常遂,會自行讀取VM參數來調整自己的一些參數,例如Berkeley DB Java Edition挽荠。但這些庫實現(xiàn)得不好的時候反而會帶來一些麻煩:BDB JE要求顯式指定-XX:+UseCompressedOops才能有效的調整它的緩存大小克胳。所以在用BDB JE并且Java堆+PermGen大小小于32GB的時候,請顯式指定-XX:+UseCompressedOops吧
- -XX:+AlwaysPreTouch
- 會把commit的空間跑循環(huán)賦值為0以達到“pretouch”的目的圈匆。開這個參數會增加VM初始化時的開銷漠另,但后面涉及虛擬內存的開銷可能降低
- -XX:+ParallelRefProcEnabled
- 這個功能可以加速reference processing,但在JDK6u25和6u26上不要使用跃赚,有bug:
Bug ID 7028845: CMS: 6984287 broke parallel reference processing in CMS
- 這個功能可以加速reference processing,但在JDK6u25和6u26上不要使用跃赚,有bug:
- -XX:+UseConcMarkSweepGC 與 -XX:+UseAdaptiveSizePolicy
- 這兩個選項在現(xiàn)有的Oracle/Sun JDK 6和Oracle JDK 7上都不要搭配在一起使用——CMS用的adaptive size policy還沒實現(xiàn)完笆搓,用的話可能會crash。
目前HotSpot VM上只有ParallelScavenge系的GC才可以配合-XX:+UseAdaptiveSizePolicy使用纬傲;也就是只有-XX:+UseParallelGC或者-XX:+UseParallelOldGC满败。Jon Masamitsu在郵件列表上提到過。
題外話:開著UseAdaptiveSizePolicy的ParallelScavenge會動態(tài)調整各空間的大小叹括,有可能會造成兩個survivor space的大小被調整得不一樣大算墨。Jon Masamitsu在這封郵件里解釋了原因。
追加:JDK9里CMS終于要徹底不支持adaptive size policy了:https://bugs.openjdk.java.net/browse/JDK-8034246
- 這兩個選項在現(xiàn)有的Oracle/Sun JDK 6和Oracle JDK 7上都不要搭配在一起使用——CMS用的adaptive size policy還沒實現(xiàn)完笆搓,用的話可能會crash。
- -XX:HeapDumpPath 與 -XX:+HeapDumpBeforeFullGC汁雷、-XX:+HeapDumpAfterFullGC米同、-XX:+HeapDumpOnOutOfMemoryError
- 但很多人都會疑惑:做出來的heap dump存到哪里去了?
如果不想費神去摸索到底各種環(huán)境被配置成什么樣摔竿、“working directory”到底在哪里的話,就在VM啟動參數里加上 -XX:HeapDumpPath=一個絕對路徑 吧
- 但很多人都會疑惑:做出來的heap dump存到哪里去了?