java啟動參數共分為三類
其一是標准參數( -
),所有的JVM實現都必須實現這些參數的功能,而且向后兼容
其二是非標准參數( -X
),默認jvm實現這些參數的功能,但是並不保證所有jvm實現都滿足,且不保證向后兼容
其三是非Stable參數( -XX
),此類參數各個jvm實現會有所不同,將來可能會隨時取消,需要慎重使用
java雖然是自動回收內存,但是應用程序,尤其服務器程序最好根據業務情況指明內存分配限制。
否則可能導致應用程序宕掉。
舉例說明含義:
-Xms128m
表示JVM Heap(堆內存)最小尺寸128MB,初始分配
-Xmx512m
表示JVM Heap(堆內存)最大允許的尺寸256MB,按需分配。
說明:如果-Xmx不指定或者指定偏小,應用可能會導致java.lang.OutOfMemory錯誤。
IDEA中配置 -Xms -Xmx 示例:
PermSize和MaxPermSize指明虛擬機為java永久生成對象(Permanate generation)
如,class對象、方法對象這些可反射(reflective)對象分配內存限制,這些內存不包括在Heap(堆內存)區之中。
-XX:PermSize=64MB 最小尺寸,初始分配
-XX:MaxPermSize=256MB 最大允許分配尺寸,按需分配
過小會導致:java.lang.OutOfMemoryError: PermGen space
MaxPermSize缺省值和-server -client選項相關。
-server選項下默認MaxPermSize為64m
-client選項下默認MaxPermSize為32m
經驗:
1、慎用最小限制選項Xms,PermSize已節約系統資源。
=========================================================
近期研究對jvm的內存使用情況進行監控,因此對觀察虛擬機的內存使用方法做了一些收集,對jvm的參數設置了解了一下:
幾個基本概念:
PermGen space:全稱是Permanent Generation space,即永久代。就是說是永久保存的區域,用於存放Class和Meta信息,Class在被Load的時候被放入該區域,GC(Garbage Collection)應該不會對PermGen space進行清理,所以如果你的APP會LOAD很多CLASS的話,就很可能出現PermGen space錯誤。
Heap space:存放Instance。Java Heap分為3個區,Young即新生代,Old即老生代和Permanent。Young保存剛實例化的對象。當該區被填滿時,GC會將對象移到Old區。Permanent區則負責保存反射對象。
幾個參數設置的意義:
xms/xmx:定義YOUNG+OLD段的總尺寸,
ms為JVM啟動時YOUNG+OLD的內存大小;
mx為最大可占用的YOUNG+OLD內存大小。
在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
NewSize/MaxNewSize:定義YOUNG段的尺寸,
NewSize為JVM啟動時YOUNG的內存大小;
MaxNewSize為最大可占用的YOUNG內存大小。
在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
PermSize/MaxPermSize:定義Perm段的尺寸,PermSize為JVM啟動時Perm的內存大小;MaxPermSize為最大可占用的Perm內存大小。在用戶生產環境上一般將這兩個值設為相同,以減少運行期間系統在內存申請上所花的開銷。
SurvivorRatio:設置YOUNG代中Survivor空間和Eden空間的比例
申請一塊內存的過程:
A. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域
B. 當Eden空間足夠時,內存申請結束。否則到下一步
C. JVM試圖釋放在Eden中所有不活躍的對象(這屬於1或更高級的垃圾回收);釋放后若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區/OLD區
D. Survivor區被用來作為Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區
E. 當OLD區空間不夠時,JVM會在OLD區進行完全的垃圾收集(0級)
F. 完全垃圾收集后,若Survivor及OLD區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現”out of memory錯誤”
我們的一種resin服務器的jvm參數設置:
“-Xmx2000M -Xms2000M -Xmn500M -XX:PermSize=250M -XX:MaxPermSize=250M -Xss256K -XX:+DisableExplicitGC -XX:SurvivorRatio=1 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSClassUnloadingEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log”
是一種典型的響應時間優先型的配置。
Java中有四種不同的回收算法,對應的啟動參數為
–XX:+UseSerialGC
–XX:+UseParallelGC
–XX:+UseParallelOldGC
–XX:+UseConcMarkSweepGC
1. Serial Collector
大部分平台或者強制 java -client 默認會使用這種。
young generation算法 = serial
old generation算法 = serial (mark-sweep-compact)
這種方法的缺點很明顯,stop-the-world, 速度慢。服務器應用不推薦使用。
2. Parallel Collector
在linux x64上默認是這種,其他平台要加 java -server 參數才會默認選用這種。
young = parallel,多個thread同時copy
old = mark-sweep-compact = 1
優點:新生代回收更快。因為系統大部分時間做的gc都是新生代的,這樣提高了throughput(cpu用於非gc時間)
缺點:當運行在8G/16G server上old generation live object太多時候pause time過長
3. Parallel Compact Collector (ParallelOld)
young = parallel = 2
old = parallel,分成多個獨立的單元,如果單元中live object少則回收,多則跳過
優點:old old generation上性能較 parallel 方式有提高
缺點:大部分server系統old generation內存占用會達到60%-80%, 沒有那么多理想的單元live object很少方便迅速回收,同時compact方面開銷比起parallel並沒明顯減少。
4. Concurent Mark-Sweep(CMS) Collector
young generation = parallel collector = 2
old = cms
同時不做 compact 操作。
優點:pause time會降低, pause敏感但CPU有空閑的場景需要建議使用策略4.
缺點:cpu占用過多,cpu密集型服務器不適合。另外碎片太多,每個object的存儲都要通過鏈表連續跳n個地方,空間浪費問題也會增大。
內存監控的方法:
1. jmap -heap pid
查看java 堆(heap)使用情況
using thread-local object allocation.
Parallel GC with 4 thread(s) //GC 方式
Heap Configuration: //堆內存初始化配置
MinHeapFreeRatio=40 //對應jvm啟動參數-XX:MinHeapFreeRatio設置JVM堆最小空閑比率(default 40)
MaxHeapFreeRatio=70 //對應jvm啟動參數 -XX:MaxHeapFreeRatio設置JVM堆最大空閑比率(default 70)
MaxHeapSize=512.0MB //對應jvm啟動參數-XX:MaxHeapSize=設置JVM堆的最大大小
NewSize = 1.0MB //對應jvm啟動參數-XX:NewSize=設置JVM堆的‘新生代’的默認大小
MaxNewSize =4095MB //對應jvm啟動參數-XX:MaxNewSize=設置JVM堆的‘新生代’的最大大小
OldSize = 4.0MB //對應jvm啟動參數-XX:OldSize=<value>:設置JVM堆的‘老生代’的大小
NewRatio = 8 //對應jvm啟動參數-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 //對應jvm啟動參數-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值
PermSize= 16.0MB //對應jvm啟動參數-XX:PermSize=<value>:設置JVM堆的‘永生代’的初始大小
MaxPermSize=64.0MB //對應jvm啟動參數-XX:MaxPermSize=<value>:設置JVM堆的‘永生代’的最大大小
Heap Usage: //堆內存分步
PS Young Generation
Eden Space: //Eden區內存分布
capacity = 20381696 (19.4375MB) //Eden區總容量
used = 20370032 (19.426376342773438MB) //Eden區已使用
free = 11664 (0.0111236572265625MB) //Eden區剩余容量
99.94277218147106% used //Eden區使用比率
From Space: //其中一個Survivor區的內存分布
capacity = 8519680 (8.125MB)
used = 32768 (0.03125MB)
free = 8486912 (8.09375MB)
0.38461538461538464% used
To Space: //另一個Survivor區的內存分布
capacity = 9306112 (8.875MB)
used = 0 (0.0MB)
free = 9306112 (8.875MB)
0.0% used
PS Old Generation //當前的Old區內存分布
capacity = 366280704 (349.3125MB)
used = 322179848 (307.25464630126953MB)
free = 44100856 (42.05785369873047MB)
87.95982001825573% used
PS Perm Generation //當前的 “永生代” 內存分布
capacity = 3
http://blog.sina.com.cn/s/blog_684fe8af0100v4mt.html
調試參數
打印啟動參數
可以查看默認參數
-
java -XX:+PrintCommandLineFlags-version
打印GC日志
不要用 XX:+UseGCLogFileRotation
,這個會丟失舊的日志文件,而且重啟會覆蓋當前日志文件:
-
-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:/home/GCEASY/gc.log -XX:+UseGCLogFileRotation-XX:NumberOfGCLogFiles=5-XX:GCLogFileSize=20M
應該用下面這個
-
-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:/home/GCEASY/gc-%t.log
打印ClassLoader日志
這個參數會在控制台打印所有類加載/卸載信息
-
-XX:+TraceClassLoading-XX:+TraceClassUnloading
OOM時Dump內存
-
-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/crashes/my-heap-dump.hprof
OOM時執行腳本(比如重啟)
-
-XX:OnOutOfMemoryError=/scripts/restart-myapp.sh
打印JIT時間
-
-XX:-CITime
方法被編譯時打印相關信息
-
-XX:-PrintCompilation
JMX
-
-Djava.net.preferIPv4Stack=true
-
-Dcom.sun.management.jmxremote
-
-Djava.rmi.server.hostname={hostname}
-
-Dcom.sun.management.jmxremote.port={port}
-
-Dcom.sun.management.jmxremote.authenticate=false
內存類
JVM設置內存的單位默認是字節(不加單位的情況下)。
也可以在大小后面增加單位,例如:
-
-Xmn256m
-
-Xmn262144k
-
-Xmn268435456
設置初始新生代大小
-
-XX:NewSize=2G(也可以是2M)
設置最大新生代大小
-
-XX:MaxNewSize=2G(也可以是2M)
注意:-Xmn
優先級大於-XX:NewRatio
設置Eden/Survivor比例
表示兩個Survivor和Edgen區的比,8表示兩個Survivor:Eden=2:8,即一個Survivor占新生代的1/10。
計算方式為:
-
SurvivorSize(1) = YoungGenerationSize/ (2+<SurvivorRatio)
-
EdenSize= YoungGenerationSize/ (2+SurvivorRatio) * SurvivorRatio
配置:
-
-XX:SurvivorRatio=8
8也是默認的比例,不過這個比例在Parallel Scavenge(新生代並行回收器,JDK5以后的默認新生代回收器)回收器下是動態的,運行時會出現Eden/Survivor比例和配置的不同。整編:微信公眾號,搜雲庫技術團隊,ID:souyunku
由於與吞吐量關系密切,Parallel Scavenge收集器也經常稱為“吞吐量優先”收集器。除上述兩個參數之外,Parallel Scavenge收集器還有一個參數-XX:+UseAdaptiveSizePolicy值得關注。這是一個開關參數,當這個參數打開之后,就不需要手工指定新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細節參數了,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱為GC自適應的調節策略(GC Ergonomics)[插圖]。如果讀者對於收集器運作原來不太了解,手工優化存在困難的時候,使用Parallel Scavenge收集器配合自適應調節策略,把內存管理的調優任務交給虛擬機去完成將是一個不錯的選擇。只需要把基本的內存數據設置好(如-Xmx設置最大堆),然后使用MaxGCPauseMillis參數(更關注最大停頓時間)或GCTimeRatio (更關注吞吐量)參數給虛擬機設立一個優化目標,那具體細節參數的調節工作就由虛擬機完成了。自適應調節策略也是Parallel Scavenge收集器與ParNew收集器的一個重要區別。
https://docs.oracle.com/javas...
設置老年代大小
老年代大小無法直接設置,只能通過堆大小+分配比例進行調整
-
#設置新老一代大小之間的比率。默認值為2。2表示New Size:Old Size=1:2,則新生代占堆大小的1/3,老年代占堆大小的2/3
-
-XX:NewRatio=2
新生代老年代大小計算方式為:
-
NewSize= HeapSize/ NewRatio+ 1
-
OldSize= (HeapSize/ NewRatio+ 1) * NewRatio
設置永久代(PermGen/MetaSpace)大小
-
#設置分配給永久生成的空間,如果超出該空間,則會觸發垃圾回收。此選項在JDK 8中已棄用,並由-XX:MetaspaceSize選項取代。
-
-XX:PermSize=size
-
#設置最大永久生成空間大小(以字節為單位)。此選項在JDK 8中已棄用,並由-XX:MaxMetaspaceSize選項取代。
-
-XX:MaxPermSize=size
-
-
#設置分配的Metaspace的大小,Metaspace將在首次超過垃圾收集時觸發垃圾收集。垃圾收集的閾值取決於使用的元數據量而增加或減少。默認大小取決於平台。
-
-XX:MetaspaceSize=size
-
-
#設置可以分配給Metaspace的最大本機內存。默認情況下,大小不受限制。應用程序的Metaspace量取決於應用程序本身,其他正在運行的應用程序以及系統上可用的內存量
-
-XX:MaxMetaspaceSize=size
初始大小和最大值的區別
初始值(比如 -Xms
)為JVM啟動是向操作系統申請的內存大小( malloc
),最大值(比如 -Xmx
)表示,當使用的內存超過初始值后擴容的最大值
PS: JVM配置了多少內存並不是說啟動后就會占用多少物理內存,因為操作系統的內存分配是惰性的。對於已申請的內存雖然會分配地址空間,但並不會直接占用物理內存,真正使用的時候才會映射到實際的物理內存。
GC類
這里說一下PermGen/Metaspace的GC,沒有查到官方資料說永久代的固定垃圾回收器,但是在stackoverflow上有人回答到:
所有垃圾回收器都會回收永久代,包括PS/CMS,但並不是每個GC周期都會清理永久代。
這個不用糾結,看GC日志里清理的信息即可。
Serial/Serial Old
最古老的,單線程,獨占式,成熟,每次GC會STW,適合單CPU 服務器
Serial是一個新生代收集器,Serial Old是Serial收集器的的老年代版本
新生代和老年代都用串行收集器
-
-XX:+UseSerialGC
新生代使用ParallerGC,老年代使用Serial Old
-
-XX:+UseParallelGC
ParNew
和Serial基本沒區別,唯一的區別:多線程,多CPU的,停頓時間比Serial少
新生代使用ParNew,老年代使用Serial Old
-
-XX:+UseParNewGC(在Java8中已棄用,在Java9中已刪除)
Parallel Scavenge(ParallerGC)/Parallel Old
關注吞吐量的垃圾收集器,高吞吐量則可以高效率地利用CPU時間,盡快完成程序的運算任務,主要適合在后台運算而不需要太多交互的任務。整編:微信公眾號,搜雲庫技術團隊,ID:souyunku
所謂吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),虛擬機總共運行了100分鍾,其中垃圾收集花掉1分鍾,那吞吐量就是99%。
Parallel Scavenge是一個新生代收集器,Parallel Old是Parallel Scavenge收集器的的老年代版本
新生代使用ParallerGC,老年代使用Parallel Old
-
-XX:+UseParallelGC
-
#等價於
-
-XX:+UseParallelOldGC
Concurrent Mark Sweep (CMS)
CMS(Concurrent Mark Sweep),收集器是一種以獲取最短回收停頓時間為目標的收集器,一個老年代垃圾回收器。目前很大一部分的Java應用集中在互聯網站或者B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。CMS收集器就非常符合這類應用的需求。
新生代使用ParNew,老年代的用CMS
-
-XX:+UseConcMarkSweepGC
G1
使用G1收集器
-
-XX:+UseG1GC
垃圾回收器的組合
下面是一些缺省的寫法
JDK7
-
JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
-
JAVA_DEBUG_OPTS=" -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof -XX:OnOutOfMemoryError=/scripts/restart-myapp.sh "
JDK8
-
JAVA_MEM_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:MetaspaceSize=256m -Xss1024m -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 "
-
JAVA_DEBUG_OPTS=" -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/home/GCEASY/gc-%t.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof -XX:OnOutOfMemoryError=/scripts/restart-myapp.sh "
關於G1,雖然說JDK8中已經支持G1了,但是並不是說一定需要。
G1的重要特點是為用戶的應用程序的提供一個低GC延時和大內存GC的解決方案,適用於大內存場景(官方推薦堆6G以上)
如果程序正在使用CMS或ParallelOld垃圾回收器,並且具有一個或多個以下特征,那么則可以考慮升級為G1:
-
Full GC持續時間太長或太頻繁
-
對象分配率或年輕代升級老年代很頻繁
-
垃圾收集時間或壓縮暫停(超過0.5至1秒)時間過長
PS:如果正在使用CMS或ParallelOld收集器,並且程序沒有遇到長時間的垃圾收集暫停,那么就不需要升級到G1
作者:空無
來源:http://suo.im/5Y8mTF