JAVA技術
  當前位置:首頁 > 技術支持 > JAVA技術
JVM--内存參數設置及常見錯誤總結
發布時(shí)間:2017-02-06 浏覽:6965次
  

一、  JVM規範

JVM規範對(duì)Java運行時(shí)的(de)内存劃定了(le)幾塊區(qū)域(詳見這(zhè)裏),有:JVM棧(Java Virtual Machine Stacks)、堆(Heap)、方法區(qū)(Method Area)、常量池(Runtime Constant Pool)、本地方法棧(Native Method Stacks),但對(duì)各塊區(qū)域的(de)内存布局和(hé)地址空間卻沒有明(míng)确規定,而留給各JVM廠商發揮的(de)空間。

二、  HotSpot JVM

Sun自家的(de)HotSpot JVM實現對(duì)堆内存結構有相對(duì)明(míng)确的(de)說明(míng)。按照(zhào)HotSpot JVM的(de)實現,堆内存分(fēn)爲3個(gè)代:Young Generation、Old(Tenured) Generation、Permanent Generation。衆所周知,GC(垃圾收集)就是發生在堆内存這(zhè)三個(gè)代上面的(de)。Young用(yòng)于分(fēn)配新的(de)Java對(duì)象,其又被分(fēn)爲三個(gè)部分(fēn):Eden Space和(hé)兩塊Survivor Space(稱爲From和(hé)To),Old用(yòng)于存放在GC過程中從Young Gen中存活下(xià)來(lái)的(de)對(duì)象,Permanent用(yòng)于存放JVM加載的(de)class等元數據。詳情參見HotSpot内存管理(lǐ)白皮書(shū)

     1.Java Heap分(fēn)爲3個(gè)區(qū)

         1).Young(分(fēn)爲兩個(gè)同等大(dà)小的(de)survior區(qū)和(hé)一個(gè)eden區(qū),JVM默認分(fēn)配内存大(dà)小爲survior:eden = 1 : 8, 可(kě)配置)

         2).Old(一個(gè)Old區(qū),JVM默認分(fēn)配内存大(dà)小爲 Old:Young = 2:1, 可(kě)配置)

         3).Permanent

         Young保存剛實例化(huà)的(de)對(duì)象。當該區(qū)被填滿時(shí),GC會将對(duì)象移到Old區(qū)(按照(zhào)一定的(de)算(suàn)法,如GC超過15次後某一對(duì)象還(hái)存活,則會移動到Old區(qū),可(kě)配置)

     2.JVM有2個(gè)GC線程 (未查找到詳細資料)

         第一個(gè)線程負責回收Heap的(de)Young區(qū)
         第二個(gè)線程在Heap不足時(shí),遍曆Heap,将Young 區(qū)升級爲Older區(qū)

         以下(xià)是我的(de)機器的(de)執行該命令查看到的(de)JVM相關線程:jstack -l pid

 

  1. "VMThread" prio=10 tid=0x00007fa2c4061000 nid=0x18431 runnable   
  2. "GCtask thread#0 (ParallelGC)" prio=10 tid=0x00007fa2c401a000 nid=0x1842frunnable  
  3. "GCtask thread#1 (ParallelGC)" prio=10 tid=0x00007fa2c401b800 nid=0x18430runnable  
  4. "VMPeriodic Task Thread" prio=10 tid=0x00007fa2c4097800 nid=0x18438 waitingon condition  
"VMThread" prio=10 tid=0x00007fa2c4061000 nid=0x18431 runnable "GCtask thread#0 (ParallelGC)" prio=10 tid=0x00007fa2c401a000 nid=0x1842frunnable "GCtask thread#1 (ParallelGC)" prio=10 tid=0x00007fa2c401b800 nid=0x18430runnable "VMPeriodic Task Thread" prio=10 tid=0x00007fa2c4097800 nid=0x18438 waitingon condition

三、  JVM參數設置

非穩态選項使用(yòng)說明(míng):
-XX:+<option> 啓用(yòng)option
-XX:-<option> 不啓用(yòng)option
-XX:<option>=<number> 設定option的(de)值爲數字類型,可(kě)跟單位,例如 32k, 1024m, 2g
-XX:<option>=<string> 設定option的(de)值爲字符串,例如-XX:HeapDumpPath=./dump.core

性能選項

選項與默認值 默認值與限制 描述
-Xms 初始堆大(dà)小 默認(MinHeapFreeRatio參數可(kě)以調整)空餘堆内存小于40%時(shí),JVM就會增大(dà)堆直到-Xmx的(de)最大(dà)限制.
-Xmx 最大(dà)堆大(dà)小 默認(MaxHeapFreeRatio參數可(kě)以調整)空餘堆内存大(dà)于70%時(shí),JVM會減少堆直到 -Xms的(de)最小限制.
注意:此處的(de)大(dà)小是(eden+ 2 survivor space).與jmap -heap中顯示的(de)New gen是不同的(de)。
整個(gè)堆大(dà)小=年輕代大(dà)小 + 年老代大(dà)小 + 持久代大(dà)小.
-Xmn 年輕代大(dà)小(1.4or lator) 增大(dà)年輕代後,将會減小年老代大(dà)小.此值對(duì)系統性能影(yǐng)響較大(dà),Sun官方推薦配置爲整個(gè)堆的(de)3/8
-Xss 每個(gè)線程的(de)堆棧大(dà)小 JDK5.0以後每個(gè)線程堆棧大(dà)小爲1M,以前每個(gè)線程堆棧大(dà)小爲256K.更具應用(yòng)的(de)線程所需内存大(dà)小進行 調整.在相同物(wù)理(lǐ)内存下(xià),減小這(zhè)個(gè)值能生成更多(duō)的(de)線程.但是操作系統對(duì)一個(gè)進程内的(de)線程數還(hái)是有限制的(de),不能無限生成,經驗值在3000~5000左右
一般小的(de)應用(yòng), 如果棧不是很深, 應該是128k夠用(yòng)的(de) 大(dà)的(de)應用(yòng)建議(yì)使用(yòng)256k。這(zhè)個(gè)選項對(duì)性能影(yǐng)響比較大(dà),需要嚴格的(de)測試。(校長(cháng))
和(hé)threadstacksize選項解釋很類似,官方文檔似乎沒有解釋,在論壇中有這(zhè)樣一句話(huà):"”
-Xss is translated in a VM flag named ThreadStackSize”
一般設置這(zhè)個(gè)值就可(kě)以了(le)。
-XX:MaxTenuringThreshold 垃圾最大(dà)年齡 如果設置爲0的(de)話(huà),則年輕代對(duì)象不經過Survivor區(qū),直接進入年老代. 對(duì)于年老代比較多(duō)的(de)應用(yòng),可(kě)以提高(gāo)效率.如果将此值設置爲一個(gè)較大(dà)值,則年輕代對(duì)象會在Survivor區(qū)進行多(duō)次複制,這(zhè)樣可(kě)以增加對(duì)象再年輕代的(de)存活 時(shí)間,增加在年輕代即被回收的(de)概率
該參數隻有在串行GC時(shí)才有效.
-XX:ParallelGCThreads 并行收集器的(de)線程數 此值最好配置與處理(lǐ)器數目相等 同樣适用(yòng)于CMS
-XX:+AggressiveOpts JDK 5 update 6後引入,但需要手動啓用(yòng)。 啓用(yòng)JVM開發團隊最新的(de)調優成果。例如編譯優化(huà),偏向鎖,并行年老代收集等。
JDK6默認啓用(yòng)。
-XX:LargePageSizeInBytes=4m 默認4m,amd64位:2m 設置堆的(de)内存頁大(dà)小。
-XX:MaxHeapFreeRatio=70 70 GC後,如果發現空閑堆内存占到整個(gè)預估堆内存的(de)70%,則收縮堆内存預估最大(dà)值。
什(shén)麽是預估堆内存?
預估堆内存是堆大(dà)小動态調控的(de)重要選項之一。堆内存預估最大(dà)值一定小于或等于固定最大(dà)值(-Xmx指定的(de)數值)。前者會根據使用(yòng)情況動态調大(dà)或縮小,以提高(gāo)GC回收的(de)效率。
-XX:NewSize 設置年輕代大(dà)小(for 1.3/1.4)
-XX:MaxNewSize=size 1.3.1 Sparc: 32m 新生代占整個(gè)堆内存的(de)最大(dà)值。
1.3.1 x86: 2.5m
-XX:MaxPermSize=64m 5.0以後: 64 bit VMs會增大(dà)預設值的(de)30% Perm占整個(gè)堆内存的(de)最大(dà)值。
1.4 amd64: 96m
1.3.1 -client: 32m
其他(tā)默認 64m
-XX:MinHeapFreeRatio=40 40 GC後,如果發現空閑堆内存占到整個(gè)預估堆内存的(de)40%,則放大(dà)堆内存的(de)預估最大(dà)值,但不超過固定最大(dà)值。
關聯選項:
-XX:MaxHeapFreeRatio=70
-XX:NewRatio=2 Sparc -client: 8 新生代和(hé)年老代的(de)堆内存占用(yòng)比例。
這(zhè)裏的(de)2表示,新生代占最大(dà)堆内存的(de)1/2。也(yě)就是和(hé)年老代平分(fēn)堆的(de)占用(yòng)。
x86 -server: 8
x86 -client: 12
-client: 4 (1.3)
8 (1.3.1+)
x86: 12
其他(tā)默認 2
-XX:NewSize=2.125m 5.0以後: 64 bit Vms會增大(dà)預設值的(de)30% 新生代預估堆内存占用(yòng)的(de)默認值。(什(shén)麽是預估堆内存?見 -XX:MaxHeapFreeRatio 處的(de)描述)
x86: 1m
x86, 5.0以後: 640k
其他(tā)默認 2.125m
-XX:ReservedCodeCacheSize=32m    Solaris 64-bit, amd64, -server x86: 48m 設置代碼緩存的(de)最大(dà)值,編譯用(yòng)。
1.5.0_06之前, Solaris 64-bit amd64: 1024m
其他(tā)默認 32m
-XX:SurvivorRatio=8 Solaris amd64: 6 Eden與Survivor的(de)占用(yòng)比例。這(zhè)裏的(de)8表示,一個(gè)survivor區(qū)占用(yòng) 1/8 的(de)新生代内存,因爲survivor有2個(gè),所以是 2/8,那麽Eden的(de)占比爲 6/8。
Sparc in 1.3.1: 25
Solaris platforms5.0以前: 32
其他(tā)默認 8
-XX:TargetSurvivorRatio=50 50 實際使用(yòng)的(de)survivor空間大(dà)小占比。默認是50%,最高(gāo)90%。
-XX:ThreadStackSize=512 Sparc: 512 線程堆棧大(dà)小
Solaris x86: 320(5.0以前 256)
Sparc 64 bit: 1024
Linux amd64: 1024 (5.0 以前 0)
其他(tā)默認 512.
-XX:+UseBiasedLocking JDK 5 update 6後引入,但需要手動啓用(yòng)。 啓用(yòng)偏向鎖。
JDK6默認啓用(yòng)。
-XX:+UseFastAccessorMethods 默認啓用(yòng) 啓用(yòng)原始類型的(de)getter方法優化(huà)。
-XX:-UseISM 默認啓用(yòng) 啓用(yòng)solaris的(de)ISM。
詳見Intimate Shared Memory.
-XX:+UseLargePages JDK 5 update 5後引入,但需要手動啓用(yòng)。 啓用(yòng)大(dà)内存分(fēn)頁。
JDK6默認啓用(yòng)。
-XX:+UseMPSS 1.4.1 之前: 不啓用(yòng) 啓用(yòng)solaris的(de)MPSS,不能與ISM同時(shí)使用(yòng)。
其餘版本默認啓用(yòng)
-XX:+StringCache 默認啓用(yòng) 啓用(yòng)字符串緩存。
-XX:AllocatePrefetchLines=1 1 與機器碼指令預讀相關的(de)一個(gè)選項,資料比較少,本文檔不做(zuò)解釋。有興趣的(de)朋友請自行閱讀官方doc。
-XX:AllocatePrefetchStyle=1 1 與機器碼指令預讀相關的(de)一個(gè)選項,資料比較少,本文檔不做(zuò)解釋。有興趣的(de)朋友請自行閱讀官方doc。
-XX:-AllowUserSignalHandlers 限于Linux和(hé)Solaris,默認不啓用(yòng) 允許爲java進程安裝信号處理(lǐ)器。
-XX:-DisableExplicitGC 默認不啓用(yòng) 禁止在運行期顯式地調用(yòng) System.gc()。
開啓該選項後,GC的(de)觸發時(shí)機将由Garbage Collector全權掌控。
需要注意的(de)是,你程序裏沒調用(yòng)System.gc(),你依賴的(de)框架,工具可(kě)能在使用(yòng)。例如RMI。請仔細權衡禁用(yòng)帶來(lái)的(de)影(yǐng)響。
-XX:-RelaxAccessControlCheck 默認不啓用(yòng) 在Class校驗器裏,放松對(duì)訪問控制的(de)檢查。作用(yòng)與reflection裏的(de)setAccessible類似。
-XX:-UseConcMarkSweepGC 默認不啓用(yòng) 啓用(yòng)CMS低停頓垃圾收集器。
-XX:-UseParallelGC 默認不啓用(yòng),-server時(shí)啓用(yòng) 策略爲新生代使用(yòng)并行清除,年老代使用(yòng)單線程Mark-Sweep-Compact清除的(de)垃圾收集器。
-XX:-UseParallelOldGC 默認不啓用(yòng) 策略爲老年代和(hé)新生代都使用(yòng)并行清除的(de)垃圾收集器。
-XX:-UseSerialGC 默認不啓用(yòng),-client時(shí)啓用(yòng) 使用(yòng)串行垃圾收集器。
-XX:+UseSplitVerifier java5默認不啓用(yòng),java6默認啓用(yòng) 使用(yòng)新的(de)Class類型校驗器 。
什(shén)麽是新Class類型校驗器?
新Class類型校驗器,将老的(de)校驗步驟拆分(fēn)成兩步:
1,類型推斷。2,類型校驗。
新類型校驗器通(tōng)過在javac編譯時(shí)嵌入類型信息到bytecode中,省略了(le)類型推斷這(zhè)一步,從而提升了(le)classloader的(de)性能。
Classload順序:load -> verify -> prepare -> resove -> init
關聯選項:
-XX:+FailOverToOldVerifier
-XX:+FailOverToOldVerifier Java6新引入選項,默認啓用(yòng) 如果新的(de)Class校驗器檢查失敗,則使用(yòng)老的(de)校驗器。
關聯選項:
-XX:+UseSplitVerifier
-XX:+HandlePromotionFailure     java5以前是默認不啓用(yòng),java6默認啓用(yòng) 關閉新生代收集擔保。
什(shén)麽是新生代收集擔保?
在一次理(lǐ)想化(huà)的(de)minor gc中,活躍對(duì)象會從Eden和(hé)First Survivor中被複制到Second Survivor。然而,Second Survivor不一定能容納所有的(de)活躍對(duì)象。爲了(le)确保minor gc能夠順利完成,需要在年老代中保留一塊足以容納所有活躍對(duì)象的(de)内存空間。這(zhè)個(gè)預留的(de)操作,被稱之爲新生代收集擔保(New Generation Guarantee)。當預留操作無法完成時(shí),就會觸發major gc(full gc)。
爲什(shén)麽要關閉新生代收集擔保?
因爲在年老代中預留的(de)空間大(dà)小,是無法精确計算(suàn)的(de)。爲了(le)确保極端情況的(de)發生,GC參考了(le)最壞情況下(xià)的(de)新生代内存占用(yòng),即Eden+First Survivor。這(zhè)種策略無疑是在浪費年老代内存,并從時(shí)序角度看,可(kě)能提前觸發Full GC。爲了(le)避免如上情況的(de)發生,JVM允許開發者關閉新生代收集擔保。
在開啓本選項後,minotr gc将不再提供新生代收集擔保,而是在出現survior或年老代不夠用(yòng)時(shí),抛出promotion failed異常。
-XX:+UseSpinning java1.4.2和(hé)1.5需要手動啓用(yòng), java6默認已啓用(yòng) 啓用(yòng)多(duō)線程自旋鎖優化(huà)。
自旋鎖優化(huà)原理(lǐ)
大(dà)家知道,Java的(de)多(duō)線程安全是基于Lock機制實現的(de),而Lock的(de)性能往往不如人(rén)意。原因是,monitorenter與monitorexit這(zhè)兩個(gè)控制多(duō)線程同步的(de)bytecode原語,是JVM依賴操作系統互斥(mutex)來(lái)實現的(de)。互斥是一種會導緻線程挂起,并在較短的(de)時(shí)間内又需要重新調度回原線程的(de),較爲消耗資源的(de)操作。爲了(le)避免進入OS互斥,Java6的(de)開發者們提出了(le)自旋鎖優化(huà)方法。自旋鎖優化(huà)的(de)原理(lǐ)是在線程進入OS互斥前,通(tōng)過CAS自旋一定的(de)次數來(lái)檢測鎖的(de)釋放。如果在自旋次數未達到預訂值前,發現鎖已被釋放,則會立即持有該鎖。
CAS檢測鎖的(de)原理(lǐ)詳見: 關聯選項:
-XX:PreBlockSpin=10
-XX:PreBlockSpin=10 -XX:+UseSpinning必須先啓用(yòng),對(duì)于java6來(lái)說已經默認啓用(yòng)了(le),這(zhè)裏默認自旋10次 控制多(duō)線程自旋鎖優化(huà)的(de)自旋次數。(什(shén)麽是自旋鎖優化(huà)?見 -XX:+UseSpinning 處的(de)描述)
關聯選項:
-XX:+UseSpinning
-XX:+ScavengeBeforeFullGC     默認啓用(yòng) 在Full GC前觸發一次Minor GC。
-XX:+UseGCOverheadLimit 默認啓用(yòng) 限制GC的(de)運行時(shí)間。如果GC耗時(shí)過長(cháng),就抛OOM。
-XX:+UseTLAB 1.4.2以前和(hé)使用(yòng)-client選項時(shí),默認不啓用(yòng),其餘版本默認啓用(yòng) 啓用(yòng)線程本地緩存區(qū)(Thread Local)。
-XX:+UseThreadPriorities 默認啓用(yòng) 使用(yòng)本地線程的(de)優先級。
-XX:+UseAltSigs 限于Solaris,默認啓用(yòng) 爲了(le)防止與其他(tā)發送信号的(de)應用(yòng)程序沖突,允許使用(yòng)候補信号替代 SIGUSR1和(hé)SIGUSR2。
-XX:+UseBoundThreads 限于Solaris, 默認啓用(yòng) 綁定所有的(de)用(yòng)戶線程到内核線程。
減少線程進入饑餓狀态(得(de)不到任何cpu time)的(de)次數。
-XX:+UseLWPSynchronization 限于solaris,默認啓用(yòng) 使用(yòng)輕量級進程(内核線程)替換線程同步。
-XX:+MaxFDLimit 限于Solaris,默認啓用(yòng) 設置java進程可(kě)用(yòng)文件描述符爲操作系統允許的(de)最大(dà)值。
-XX:+UseVMInterruptibleIO 限于solaris,默認啓用(yòng) 在solaris中,允許運行時(shí)中斷線程 。
-XX:CMSInitiatingOccupancyFraction=70 92 使用(yòng)cms作爲垃圾回收 使用(yòng)70%後開始CMS收集 爲了(le)保證不出現promotion failed(見下(xià)面介紹)錯誤,該值的(de)設置需要滿足以下(xià)公式CMSInitiatingOccupancyFraction計算(suàn)公式

 

調試選項

選項與默認值 默認值與限制 描述
-XX:-CITime 1.4引入。 打印JIT編譯器編譯耗時(shí)。
默認啓用(yòng)
-XX:ErrorFile=./hs_err_pid<pid>.log Java 6引入。 如果JVM crash後,将錯誤日志輸出到指定目錄。
-XX:-ExtendedDTraceProbes Java6引入,限于solaris 啓用(yòng)dtrace診斷。
默認不啓用(yòng)
-XX:HeapDumpPath=./java_pid<pid>.hprof    默認是java進程啓動位置,即user.dir 堆内存快(kuài)照(zhào)的(de)存儲路徑。
什(shén)麽是堆内存快(kuài)照(zhào)?
當java進程因OOM或crash被強制退出後,生成hprof(Heap PROFling)格式的(de)堆快(kuài)照(zhào)文件。用(yòng)于出問題後調試,診斷。文件名一般爲java_<pid>_<date>_<time>_heapDump.hprof.
解析快(kuài)照(zhào)文件,可(kě)以使用(yòng) jhat, eclipse MAT,gdb等工具。
-XX:-HeapDumpOnOutOfMemoryError 1.4.2 update12 和(hé) 5.0 update 7 引入。 在OOM時(shí),輸出一個(gè)dump.core文件,記錄當時(shí)的(de)堆内存快(kuài)照(zhào)(什(shén)麽是堆内存快(kuài)照(zhào)? 見 -XX:HeapDumpPath 處的(de)描述)。
默認不啓用(yòng)
-XX:OnError="<cmd args>;<cmd args>" 1.4.2 update 9引入 當java每抛出一個(gè)ERROR時(shí),運行指定命令行指令集。指令集是與OS環境相關的(de),在linux下(xià)多(duō)數是bash腳本,windows下(xià)是某個(gè)dos批處理(lǐ)。
-XX:OnOutOfMemoryError="<cmd args>;<cmd args>" 1.4.2 update 12和(hé)java6時(shí)引入 當第一次OOM時(shí),運行指定命令行指令集。指令集是與OS環境相關的(de),在linux下(xià)多(duō)數是bash腳本,windows下(xià)是某個(gè)dos批處理(lǐ)。
-XX:-PrintClassHistogram 默認不啓用(yòng) 打印class柱狀圖,圖中除了(le)class,還(hái)有該class的(de)instance統計信息。
Windows下(xià), 按ctrl-break時(shí)。
Linux下(xià)是執行kill -3,或發送SIGQUIT信号。
Jmap ?histo pid也(yě)實現了(le)相同的(de)功能。詳見 http://java.sun.com/javase/6/docs/technotes/tools/share/jmap.html
-XX:-PrintConcurrentLocks 默認不啓用(yòng) 在線程dump時(shí),順便打印java.util.concurrent鎖狀态。
Jstack ?l pid 也(yě)同樣實現了(le)相同的(de)功能。詳見 http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html
-XX:-PrintCommandLineFlags 5.0 引入,默認不啓用(yòng) Java啓動時(shí),往stdout打印當前啓用(yòng)的(de)非穩态jvm options。
例如:
-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:+DoEscapeAnalysis
-XX:-PrintCompilation 默認不啓用(yòng) 打印方法被JIT編譯時(shí)的(de)信息到stdout。
例如:
java.lang.String::charAt (33 bytes)
-XX:-PrintGC 默認不啓用(yòng) 開啓GC日志打印。
例如:
[Full GC 131115K->7482K(1015808K), 0.1633180 secs]
該選項可(kě)通(tōng)過 com.sun.management.HotSpotDiagnosticMXBean API 和(hé) Jconsole 動态啓用(yòng)。詳見 http://java.sun.com/developer/technicalArticles/J2SE/monitoring/#Heap_Dump
-XX:-PrintGCDetails 1.4.0引入,默認不啓用(yòng) 打印GC回收的(de)細節。
例如:
[Full GC (System) [Tenured: 0K->2394K(466048K), 0.0624140 secs] 30822K->2394K(518464K), [Perm : 10443K->10443K(16384K)], 0.0625410 secs] [Times: user=0.05 sys=0.01, real=0.06 secs]
該選項可(kě)通(tōng)過 com.sun.management.HotSpotDiagnosticMXBean API 和(hé) Jconsole 動态啓用(yòng)。詳見 http://java.sun.com/developer/technicalArticles/J2SE/monitoring/#Heap_Dump
-XX:-PrintGCTimeStamps 默認不啓用(yòng) 打印GC停頓耗時(shí)。
例如:
2.744: [Full GC (System) 2.744: [Tenured: 0K->2441K(466048K), 0.0598400 secs] 31754K->2441K(518464K), [Perm : 10717K->10717K(16384K)], 0.0599570 secs] [Times: user=0.06 sys=0.00, real=0.06
secs]
該選項可(kě)通(tōng)過 com.sun.management.HotSpotDiagnosticMXBean API 和(hé) Jconsole 動态啓用(yòng)。詳見 http://java.sun.com/developer/technicalArticles/J2SE/monitoring/#Heap_Dump
-XX:-PrintTenuringDistribution 默認不啓用(yòng) 打印對(duì)象的(de)存活期限信息。
例如:
[GC
Desired survivor size 4653056 bytes, new threshold 32 (max 32)
- age 1: 2330640 bytes, 2330640 total
- age 2: 9520 bytes, 2340160 total
204009K->21850K(515200K), 0.1563482 secs]
Age1 2表示在第1和(hé)2次GC後存活的(de)對(duì)象大(dà)小。
-XX:-TraceClassLoading 默認不啓用(yòng) 打印class裝載信息到stdout。記Loaded狀态。
例如:
[Loaded java.lang.Object from /opt/taobao/install/jdk1.6.0_07/jre/lib/rt.jar]
-XX:-TraceClassLoadingPreorder 1.4.2引入,默認不啓用(yòng) 按class的(de)引用(yòng)/依賴順序打印類裝載信息到stdout。不同于 TraceClassLoading,本選項隻記 Loading狀态。
例如:
[Loading java.lang.Object from /home/confsrv/jdk1.6.0_14/jre/lib/rt.jar]
-XX:-TraceClassResolution 1.4.2引入,默認不啓用(yòng) 打印所有靜态類,常量的(de)代碼引用(yòng)位置。用(yòng)于debug。
例如:
RESOLVE java.util.HashMap java.util.HashMap$Entry HashMap.java:209
說明(míng)HashMap類的(de)209行引用(yòng)了(le)靜态類 java.util.HashMap$Entry
-XX:-TraceClassUnloading 默認不啓用(yòng) 打印class的(de)卸載信息到stdout。記Unloaded狀态。
-XX:-TraceLoaderConstraints Java6 引入,默認不啓用(yòng) 打印class的(de)裝載策略變化(huà)信息到stdout。
例如:
[Adding new constraint for name: java/lang/String, loader[0]: sun/misc/Launcher$ExtClassLoader, loader[1]: <bootloader> ]
[Setting class object in existing constraint for name: [Ljava/lang/Object; and loader sun/misc/Launcher$ExtClassLoader ]
[Updating constraint for name org/xml/sax/InputSource, loader <bootloader>, by setting class object ]
[Extending constraint for name java/lang/Object by adding loader[15]: sun/reflect/DelegatingClassLoader  ]
裝載策略變化(huà)是實現classloader隔離/名稱空間一緻性的(de)關鍵技術。
-XX:+PerfSaveDataToFile 默認啓用(yòng) 當java進程因OOM或crash被強制退出後,生成一個(gè)堆快(kuài)照(zhào)文件(什(shén)麽是堆内存快(kuài)照(zhào)? 見 -XX:HeapDumpPath 處的(de)描述)。

 

四、  經驗

     1.年輕代大(dà)小選擇

         1)響應時(shí)間優先的(de)應用(yòng):盡可(kě)能設大(dà),直到接近系統的(de)最低響應時(shí)間限制(根據實際情況選擇).在此種情況下(xià),年輕代收集發生的(de)頻(pín)率也(yě)是最小的(de).同時(shí),減少到達年老代的(de)對(duì)象.

         2)吞吐量優先的(de)應用(yòng):盡可(kě)能的(de)設置大(dà),可(kě)能到達Gbit的(de)程度.因爲對(duì)響應時(shí)間沒有要求,垃圾收集可(kě)以并行進行,一般适合8CPU以上的(de)應用(yòng).

         3)避免設置過小.當新生代設置過小時(shí)會導緻:1.YGC次數更加頻(pín)繁 2.可(kě)能導緻YGC對(duì)象直接進入舊(jiù)生代,如果此時(shí)舊(jiù)生代滿了(le),會觸發FGC.

     2.年老代大(dà)小選擇

         1)響應時(shí)間優先的(de)應用(yòng):年老代使用(yòng)并發收集器,所以其大(dà)小需要小心設置,一般要考慮并發會話(huà)率和(hé)會話(huà)持續時(shí)間等一些參數.如果堆設置小了(le),可(kě)以會造成内存碎 片,高(gāo)回收頻(pín)率以及應用(yòng)暫停而使用(yòng)傳統的(de)标記清除方式;如果堆大(dà)了(le),則需要較長(cháng)的(de)收集時(shí)間.最優化(huà)的(de)方案,一般需要參考以下(xià)數據獲得(de): 并發垃圾收集信息、持久代并發收集次數、傳統GC信息、花在年輕代和(hé)年老代回收上的(de)時(shí)間比例。

         2)吞吐量優先的(de)應用(yòng):一般吞吐量優先的(de)應用(yòng)都有一個(gè)很大(dà)的(de)年輕代和(hé)一個(gè)較小的(de)年老代.原因是,這(zhè)樣可(kě)以盡可(kě)能回收掉大(dà)部分(fēn)短期對(duì)象,減少中期的(de)對(duì)象,而年老代盡存放長(cháng)期存活對(duì)象.

     3.較小堆引起的(de)碎片問題:promotion failed

         1)因爲年老代的(de)并發收集器使用(yòng)标記,清除算(suàn)法,所以不會對(duì)堆進行壓縮.當收集器回收時(shí),他(tā)會把相鄰的(de)空間進行合并,這(zhè)樣可(kě)以分(fēn)配給較大(dà)的(de)對(duì)象.但是,當堆空間較小時(shí),運行一段時(shí)間以後,就會出現"碎片",如果并發收集器找不到足夠的(de)空間,那麽并發收集器将會停止,然後使用(yòng)傳統的(de)标記,清除方式進行回收.如果出現"碎片",可(kě)能需要進行如下(xià)配置:

-XX:+UseCMSCompactAtFullCollection:使用(yòng)并發收集器時(shí),開啓對(duì)年老代的(de)壓縮.

-XX:CMSFullGCsBeforeCompaction=0:上面配置開啓的(de)情況下(xià),這(zhè)裏設置多(duō)少次Full GC後,對(duì)年老代進行壓縮

     4.用(yòng)64位操作系統Linux下(xià)64位的(de)jdk比32位jdk要慢(màn)一些,但是吃(chī)得(de)内存更多(duō),吞吐量更大(dà)

     5.XMX和(hé)XMS設置一樣大(dà),MaxPermSize和(hé)MinPermSize設置一樣大(dà),這(zhè)樣可(kě)以減輕伸縮堆大(dà)小帶來(lái)的(de)壓力

     6.使用(yòng)CMS的(de)好處是用(yòng)盡量少的(de)新生代,經驗值是128M-256M, 然後老生代利用(yòng)CMS并行收集, 這(zhè)樣能保證系統低延遲的(de)吞吐效率。 實際上cms的(de)收集停頓時(shí)間非常的(de)短,2G的(de)内存, 大(dà)約20-80ms的(de)應用(yòng)程序停頓時(shí)間

     7.系統停頓的(de)時(shí)候可(kě)能是GC的(de)問題也(yě)可(kě)能是程序的(de)問題,多(duō)用(yòng)jmap和(hé)jstack查看,或者killall -3 java,然後查看java控制台日志,能看出很多(duō)問題。(相關工具的(de)使用(yòng)方法将在後面的(de)blog中介紹)

     8.增加Heap的(de)大(dà)小雖然會降低GC的(de)頻(pín)率,但也(yě)增加了(le)每次GC的(de)時(shí)間。并且GC運行時(shí),所有的(de)用(yòng)戶線程将暫停,也(yě)就是GC期間,Java應用(yòng)程序不做(zuò)任何工作。

     9.Heap大(dà)小并不決定進程的(de)内存使用(yòng)量。進程的(de)内存使用(yòng)量要大(dà)于-Xmx定義的(de)值,因爲Java爲其他(tā)任務分(fēn)配内存,例如每個(gè)線程的(de)Stack等。

每個(gè)線程都有他(tā)自己的(de)Stack。-Xss

Stack的(de)大(dà)小限制著(zhe)線程的(de)數量。如果Stack過大(dà)就好導緻内存溢漏。-Xss參數決定Stack大(dà)小,例如-Xss1024K。如果Stack太小,也(yě)會導緻Stack溢漏。

     10.Java線程的(de)内存是位于JVM或操作系統的(de)棧(Stack)空間中,不同于對(duì)象――是位于堆(Heap)中。這(zhè)是很多(duō)新手程序員(yuán)容易誤解的(de)地方。注意,“Java線程的(de)内存”這(zhè)個(gè)用(yòng)詞不是指Java.lang.Thread對(duì)象的(de)内存,java.lang.Thread對(duì)象本身是在Heap中分(fēn)配的(de),當調用(yòng)start()方法之後,JVM會創建一個(gè)執行單元,最終會創建一個(gè)操作系統的(de)native thread來(lái)執行,而這(zhè)個(gè)執行單元或native thread是使用(yòng)Stack内存空間的(de)。

     11.VM進程的(de)内存大(dà)緻分(fēn)爲Heap空間和(hé)Stack空間兩部分(fēn)。Heap又分(fēn)爲Young、Old、Permanent三個(gè)代。Stack分(fēn)爲Java方法棧和(hé)native方法棧(不做(zuò)區(qū)分(fēn)),在Stack内存區(qū)中,可(kě)以創建多(duō)個(gè)線程棧,每個(gè)線程棧占據Stack區(qū)中一小部分(fēn)内存,線程棧是一個(gè)LIFO數據結構,每調用(yòng)一個(gè)方法,會在棧頂創建一個(gè)Frame,方法返回時(shí),相應的(de)Frame會從棧頂移除(通(tōng)過移動棧頂指針)。在這(zhè)每一部分(fēn)内存中,都有可(kě)能會出現溢出錯誤

     12.仔細了(le)解自己的(de)應用(yòng),如果用(yòng)了(le)緩存,那麽年老代應該大(dà)一些,緩存的(de)HashMap不應該無限制長(cháng),建議(yì)采用(yòng)LRU算(suàn)法的(de)Map做(zuò)緩存,LRUMap的(de)最大(dà)長(cháng)度也(yě)要根據實際情況設定。

     13.采用(yòng)并發回收時(shí),年輕代小一點,年老代要大(dà),因爲年老大(dà)用(yòng)的(de)是并發回收,即使時(shí)間長(cháng)點也(yě)不會影(yǐng)響其他(tā)程序繼續運行,網站不會停頓

     14.JVM參數的(de)設置(特别是 ?Xmx ?Xms ?Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold等參數的(de)設置沒有一個(gè)固定的(de)公式,需要根據PV old區(qū)實際數據 YGC次數等多(duō)方面來(lái)衡量。爲了(le)避免promotion faild可(kě)能會導緻xmn設置偏小,也(yě)意味著(zhe)YGC的(de)次數會增多(duō),處理(lǐ)并發訪問的(de)能力下(xià)降等問題。每個(gè)參數的(de)調整都需要經過詳細的(de)性能測試,才能找到特定應用(yòng)的(de)最佳配置。

五、  常見内存錯誤及解決方案

     1.OutOfMemoryError在開發過程中是司空見慣的(de),遇到這(zhè)個(gè)錯誤,新手程序員(yuán)都知道從兩個(gè)方面入手來(lái)解決:一是排查程序是否有BUG導緻内存洩漏;二是調整JVM啓動參數增大(dà)内存。OutOfMemoryError有好幾種情況,每次遇到這(zhè)個(gè)錯誤時(shí),觀察OutOfMemoryError後面的(de)提示信息,就可(kě)以發現不同之處,如:

  1. java.lang.OutOfMemoryError: Java heap space  
  2. java.lang.OutOfMemoryError: unable to create newnative thread  
  3. java.lang.OutOfMemoryError: PermGen space  
  4. java.lang.OutOfMemoryError: Requested array sizeexceeds VM limit  
java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: unable to create newnative thread java.lang.OutOfMemoryError: PermGen space java.lang.OutOfMemoryError: Requested array sizeexceeds VM limit

     2.java.lang.OutOfMemoryError:Java heap space

         1)原因:Heap内存溢出,意味著(zhe)Young和(hé)Old generation的(de)内存不夠。

         2)解決:調整java啓動參數 -Xms -Xmx 來(lái)增加Heap内存。

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

         1)原因:Stack空間不足以創建額外的(de)線程,要麽是創建的(de)線程過多(duō),要麽是Stack空間确實小了(le)。

         2)解決:由于JVM沒有提供參數設置總的(de)stack空間大(dà)小,但可(kě)以設置單個(gè)線程棧的(de)大(dà)小;而系統的(de)用(yòng)戶空間一共是3G,除了(le)Text/Data/BSS/MemoryMapping幾個(gè)段之外,Heap和(hé)Stack空間的(de)總量有限,是此消彼長(cháng)的(de)。因此遇到這(zhè)個(gè)錯誤,可(kě)以通(tōng)過兩個(gè)途徑解決:1.通(tōng)過-Xss啓動參數減少單個(gè)線程棧大(dà)小,這(zhè)樣便能開更多(duō)線程(當然不能太小,太小會出現StackOverflowError);2.通(tōng)過-Xms -Xmx 兩參數減少Heap大(dà)小,将内存讓給Stack(前提是保證Heap空間夠用(yòng))。

     4.java.lang.OutOfMemoryError:PermGen space

         1)原因:Permanent Generation空間不足,不能加載額外的(de)類。

         2)解決:調整-XX:PermSize= -XX:MaxPermSize= 兩個(gè)參數來(lái)增大(dà)PermGen内存。一般情況下(xià),這(zhè)兩個(gè)參數不要手動設置,隻要設置-Xmx足夠大(dà)即可(kě),JVM會自行選擇合适的(de)PermGen大(dà)小。

     5.java.lang.OutOfMemoryError:Requested array size exceeds VM limit

         1)原因:這(zhè)個(gè)錯誤比較少見(試著(zhe)new一個(gè)長(cháng)度1億的(de)數組看看),同樣是由于Heap空間不足。如果需要new一個(gè)如此之大(dà)的(de)數組,程序邏輯多(duō)半是不合理(lǐ)的(de)。

         2)解決:修改程序邏輯吧。或者也(yě)可(kě)以通(tōng)過-Xmx來(lái)增大(dà)堆内存。

     6.java.lang.OutOfMemoryError: GC overhead limit exceeded

         1)原因:在GC花費了(le)大(dà)量時(shí)間,卻僅回收了(le)少量内存時(shí),也(yě)會報出OutOfMemoryError,我隻遇到過一兩次。當使用(yòng)-XX:+UseParallelGC或-XX:+UseConcMarkSweepGC收集器時(shí),在上述情況下(xià)會報錯,在HotSpot GC Turning文檔上有說明(míng):

The parallel(concurrent) collector will throwan OutOfMemoryError if too much time is being spent in garbage collection: ifmore than 98% of the total time is spent in garbage collection and less than 2%of the heap is recovered, an OutOfMemoryError will be thrown.

對(duì)這(zhè)個(gè)問題,一是需要進行GC turning,二是需要優化(huà)程序邏輯。

     7.java.lang.StackOverflowError

         1)原因:這(zhè)也(yě)内存溢出錯誤的(de)一種,即線程棧的(de)溢出,要麽是方法調用(yòng)層次過多(duō)(比如存在無限遞歸調用(yòng)),要麽是線程棧太小。

         2)解決:優化(huà)程序設計,減少方法調用(yòng)層次;調整-Xss參數增加線程棧大(dà)小。

     8.concurrentmode failure

         1)原因:原文是這(zhè)樣描述的(de)(if theconcurrent collector is unable tofinish reclaiming the unreachable objectsbefore the tenured generation fillsup, or if an allocation cannot be satisfiedwith the available free space blocksin the tenured generation, then theapplication is paused and the collection iscompleted with all the applicationthreads stopped),簡單解釋就是old gen剩餘的(de)内存不足以滿足來(lái)自于young gen的(de)垃圾回收,導緻jvm通(tōng)過卸載已經生成的(de)反射類來(lái)釋放足夠的(de)内存。這(zhè)種現象會造成應用(yòng)較長(cháng)時(shí)間的(de)中斷,從而影(yǐng)響性能。所以理(lǐ)論上應該保證eden + from survivor < old gen區(qū)剩餘内存。

         2)通(tōng)過設置-XX:CMSInitiatingOccupancyFraction=50(此值的(de)初始值爲10,即預留10%的(de)空間給minor collection),當old gen已經收集了(le)50%的(de)内存後,就開始進行major collection,從而保證old gen 始終預留50%的(de)可(kě)用(yòng)内存。

         3)這(zhè)種設置雖然會提高(gāo)major collection的(de)頻(pín)率,但是根據目前major collection的(de)頻(pín)率來(lái)看(大(dà)概幾個(gè)小時(shí)一次)足以承受。

 
 
    
 
 
版權所有 © 2005-2024 安徽問法信息技術有限公司  
地址:安徽省合肥市蜀山區(qū)望江西路69号西湖國際廣場(chǎng) 電話(huà):0551-64935878 郵箱:admin@ajsoft.cn