JVM调优笔记:认识JVM内存模型(jdk1.8)
文章目錄
- 1、什么是JVM
- 2、jdk、jre、jvm關系
- 3、JVM執(zhí)行過程
- 4、JVM執(zhí)行程序的過程
- 5、JVM運行時數據區(qū)
- 虛擬機棧(線程私有)
- 本地方法棧(線程私有)
- 程序計數器(線程私有)
- 堆(線程共享)
- 方法區(qū)(線程共享)
- 6、內存分配參數
- 大小分配
- 比例分配
- 7、垃圾回收
- 算法與思想
- 分類
- 新生代串行收集器 Serial
- 老年代串行收集器 Serial Old
- 新生代并行收集器 ParNew
- 新生代并行回收收集器 Parallel Scavenge
- 老年代并行回收收集器 Parallel Old
- CMS收集器
- G1收集器
- 8、常見調優(yōu)方法
- 9、其他實用JVM參數
1、什么是JVM
(1)JVM即Java Virtual Machine,java虛擬機。它是一個虛構出來的機器,是通過實際計算機上的仿真模擬各種功能實現的。
(2)JVM包含了一套字節(jié)碼指令集,一組寄存器、一個占、一個垃圾回收堆和一個存儲方法域。
(3)JVM支持java程序跨平臺。因為它屏蔽了與具體操作系統(tǒng)平臺相關信息,使我們的Java程序只需要生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上運行。
JVM在執(zhí)行字節(jié)碼時,實際上最終還是把字節(jié)碼解釋成具體平臺上的機器指令來執(zhí)行。
2、jdk、jre、jvm關系
(1)JRE(Java Runtime Environment),也就是java平臺。所有的java程序都要在JRE環(huán)境下才能運行。
(2)JDK(Java Development Kit),是開發(fā)者用來編譯、調試程序用的開發(fā)包。JDK也是JAVA程序需要在JRE上運行。
(3)JVM(Java Virtual Machine),是JRE的一部分。它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。
JVM有自己完善的硬件架構,如處理器、堆棧、寄存器等,還具有相應的指令系統(tǒng)。
Java語言最重要的特點就是跨平臺運行。使用JVM就是為了支持與操作系統(tǒng)無關,實現跨平臺。
3、JVM執(zhí)行過程
(1)jvm是java的核心和基礎,在java編譯器和os平臺之間的虛擬處理器,可在上面執(zhí)行字節(jié)碼程序。
(2)java編譯器只要面向jvm,生成jvm能理解的字節(jié)碼文件。java源文件經編譯成字節(jié)碼程序,通過jvm將每條指令翻譯成不同的機器碼,通過特定平臺運行。
4、JVM執(zhí)行程序的過程
第一步:程序員寫出各種java文件 通過編譯器編譯成class字節(jié)碼文件
第二步:然后我們通過tomcat 或者java -jar的形式在linux或windows上運行
第三步:前提安裝了jdk 我們的jar或war程序就整體是一個jvm,在運行時會首先去讓最頂層的父層 啟動類加載器(BootStrap ClassLoader)去java的lib包下加載核心類庫。然后通過 Extension ClassLoader 擴展類加載器 在lib/ext下加載擴展類 ,再然后通過Application ClassLoader應用程序類加載器 加載我們自己的java程序中的class字節(jié)碼文件 。。。。。其中加載過程中會有雙親委派機制,就是當前應用程序類加載器向上層父級擴展類中找尋是否該類應由其執(zhí)行,擴展類加載器在向上。到達啟動類加載器去核心類庫中找尋是否是自己的類。如果是則有自己進行類加載進jvm內存方法區(qū)中。如果沒有則再向下進行委派。直到找到是屬于各級加載器自己的類。如果每一層都沒有。那么由發(fā)起方自己去執(zhí)行該類(類加載進方法區(qū))
第四部:如果我們是通過tomcat加載的話。tomcat會將應用程序解析,驗證,通過自定義類加載加載如tomcat自己的jvm方法區(qū)。
第五步:放入方法區(qū)的class類會被字節(jié)碼引擎執(zhí)行字節(jié)碼指令進行各種操作(讀寫),過程中會有程序計數器來記錄指令執(zhí)行的位置。
第六步:執(zhí)行過程從各個方法也就是線程執(zhí)行起始,該對象在建立的時候會被放入堆內存中。
第七步:初始化的對象 都會有自己的成員變量,賦完默認值,會將成員變量進行壓棧,進入java虛擬機棧中成為棧幀。并且每一個成員變量都在棧內存中指向堆內存的對象。堆棧相互關聯。
第八步:垃圾回收時刻監(jiān)測堆內存中是否有未被棧內存中成員變量指向的對象。如果有,則進行垃圾回收。
第九步:而在方法去的類和棧中的棧幀什么時候被回收呢?首先該類所有的實例化對象都在堆內存中被回收。其次加載該類的類加載器已經被回收,最后對該類的class文件沒有任何堆內存的線程或線程中變量引用。這三種情況全部滿足,棧和方法區(qū)的線程和類都全部被回收了。
5、JVM運行時數據區(qū)
從圖來看,我們可以把Java內存區(qū)分為堆內存(Heap)和棧內存(Stack)。雖然這種分法比較粗糙,實際上要復雜的多,不過初學者來說這是我們最關注的的兩塊區(qū)域。
總內存=堆內存(Xmx)+方法區(qū)內存(MaxPermSize)+棧內存(Xss)*線程數+直接內存(MaxDirectMemorySize,堆外)+虛擬機內存
虛擬機棧(線程私有)
(1)Java棧中存放的是一個個的棧幀,每個棧幀對應一個被調用的方法,在棧幀中包括局部變量表(Local Variables)、操作數棧(Operand Stack)
(2)指向當前方法所屬的類的運行時常量池(運行時常量池的概念在方法區(qū)部分會談到)的引用(Reference to runtime constant pool)
(3)方法返回地址(Return Address)和一些額外的附加信息。當線程執(zhí)行一個方法時,就會隨之創(chuàng)建一個對應的棧幀,并將建立的棧幀壓棧。當方法執(zhí)行完畢之后,便會將棧幀出棧。
這塊區(qū)域存在兩種異常情況:
(1)如果線程請求的棧深度大于虛擬機允許的深度,拋出StackOverflowError異常;
(2)如果虛擬機棧可以動態(tài)擴展,且擴展時無法申請到足夠的內存時會拋出OutOfMemoryError異常。
在這里也說一個題外話,由于每個方法從進入到返回對應著棧幀的壓入和彈出,這個過程需要耗費一定的時間和資源,所以也意味著代碼中調用的方法越多,執(zhí)行效率也會越低。所以拆分方法的也不是越多越好的。
本地方法棧(線程私有)
本地方法棧和Java虛擬機棧的功能很相似,Java虛擬機棧用于管理Java函數的調用,而本地方法棧用于管理Native方法的調用。本地方法并不是用Java實現的,而是使用C實現的。在HotSpot虛擬機中,不區(qū)分本地方法棧和虛擬機棧,因此和虛擬機棧一樣,它也會拋出StackOverFlowError和OutOfMemoryError
程序計數器(線程私有)
(1)由于在JVM中,多線程是通過線程輪流切換來獲得CPU執(zhí)行時間的.
(2)在任一具體時刻,一個CPU的內核只會執(zhí)行一條線程中的指令
(3)為了能夠使得每個線程都在線程切換后能夠恢復在切 換之前的程序執(zhí)行位置,每個線程都需要有自己獨立的程序計數器,并且不能互相被干擾
程序計數器是每個線程所私有的,由于程序計數器中存儲的數據所占空間的大小不會隨程序的執(zhí)行而發(fā)生改變,因此,對于程序計數器是不會發(fā)生內存溢出現象(OutOfMemory)的。
堆(線程共享)
(1)Java堆可以說是Java運行時內存中最重要的部分,是被所有線程共享的一塊區(qū)域幾乎所有的對象和數組都是在堆空間中分配空間的,但是隨著JIT編譯器的發(fā)展與逃逸分析技術逐漸成熟,棧上分配、標量替換優(yōu)化技術導致一些微妙的變化,所有對象堆上分配也漸漸變得不那么”絕對”了。
(2)Java堆分為新生代和老年代兩個部分,新生代又可進一步分為eden、Survivor space0(from space)和survivor space1(to space)。eden意為伊甸園,即對象的出生地,s0和s1為survivor空間,可意為幸存者,如果幸存者的對象到了指定年齡仍未被回收,則有機會進入老年代(tenured)。
默認值:
老年代:2/3的堆空間
年輕代:1/3的堆空間
eden區(qū):8/10 的年輕代
survivor0: 1/10 的年輕代
survivor1:1/10的年輕代
方法區(qū)(線程共享)
(1)被所有方法線程共享的一塊內存區(qū)域。
(2)用于存儲已經被虛擬機加載的類信息,常量,靜態(tài)變量等。
(3)這個區(qū)域的內存回收目標主要針對常量池的回收和堆類型的卸載。
在JDK1.8之前HotSpot的實現中是用永久代實現的,雖然叫永久代但同樣也是可以被GC回收的,只是對于GC的表現也和堆空間略有不同,通常對永久代GC的回收從兩個方面分析,一是GC對永久代常量池的回收,二是永久代對類元數據的回收(需要虛擬機確認該類的所有實例已被回收并不會再被使用,并且加載該類的ClassLoader已經被回收,GC就有可能回收該類型);而在JDK1.8之后永久代被拋棄,使用元空間。
6、內存分配參數
大小分配
最大堆內存:-Xmx,指新生代和老年代的大小之和的最大值
最小堆內存:-Xms,Java應用程序運行時,首先會被分配的內存大小,無法滿足時會向操作系統(tǒng)申請更多的內存。JVM會試圖將系統(tǒng)內存盡可能限制在-Xms中,因此當內存實際使用量觸及-Xms指定大小時,會觸發(fā)Full GC,因此把-Xms設置為-Xmx時,可以在系統(tǒng)運行初期減少GC次數和耗時
新生代:-Xmn,設置新生代的大小會影響老年代的大小,這個參數對系統(tǒng)性能與GC有很大的影響。一般新生代設置為整個堆內存空間的1/4到1/3左右。在HotSpot中,可以用-XX:NewSize和-XX:MaxNewSize分別設置新生代初始值和最大值,但是一般用-Xmn統(tǒng)一設置就足夠了
永久代:-XX:PermSize和-XX:MaxPermSize,在JDK1.8之前,可以分別設置永久代的初始大小和最大值
元空間:-XX:MetaspaceSize和-XX:MaxMetaspaceSize,在JDK1.8之后使用元空間,元空間的大小僅受本地內存限制,但可以分別設置元空間的初始大小和最大值(默認沒有限制)
每個線程堆棧:-Xss,在線程中進行局部變量分配,函數調用時都需要在棧中開辟空間。如果棧的空間分配太小,那線程運行中可能沒有足夠空間分配局部變量或達不到足夠的深度導致異常退出;如果??臻g過大,那么開設線程所需的內存成本上升,系統(tǒng)所能支持的線程總數也會下降
Direct內存:-XX:MaxDirectMemorySize,javaNIO中通過Direct內存來提高性能,這個區(qū)域的大小默認是64M,在適當的場景可以設置大一些
比例分配
新生代:-XX:SurvivorRatio,用于設置新生代中,eden空間和s0空間的比例關系,其中s0與s1空間大小是相同的,只能也是一樣的,并在MinorGC之后會互換角色,因此值為eden/s0 = eden/s1
新生代與老年代:-XX:NewRatio,用來設置老年代/新生代之間的比例
survivior使用率:-XX:TargetSurvivorRatio,設置survivior區(qū)的可使用率,當使用率達到這個數值時,會將對象送入老年代
新生代存活次數:-XX:MaxTenuringThreshold,在新生代中對象存活次數(經過Minor GC的次數)后仍然存活,就會晉升到老年代
直接進入老年代:-XX:PretenureSizeThreshold,設置大對象直接進入老年代的閾值,當對象的大小超過這個值時直接在老年代分配
堆空閑比例:-XX:MinHeapFreeRatio與-XX:MaxHeapFreeRatio,設置堆空間的對最小/最大空閑比例。當堆空間的空閑內存大于/小于對應值時,JVM便會擴展/壓縮堆空間大小
元空間空閑比例:-XX:MinMetaspaceFreeRatio與-XX:MaxMetaspaceFreeRatio,設置最小/最大的Metaspace剩余空間容量的百分比,Metaspace GC之后,用來控制擴展/壓縮元空間大小
可以使用-XX:PrintGCDetails來打印出堆的實際大小
7、垃圾回收
算法與思想
引用計數法:最經典也是最古老的一種垃圾收集方法。引用計數器,只需要為每個對象配備一個整形的計數器即可,但是引用計數器有個嚴重的問題,即無法處理循環(huán)引用問題。因此在Java的垃圾回收器中沒有使用這種算法
標記-清除算法(Mark-Sweep):是現代垃圾回收算法的思想基礎。將垃圾回收分為兩個階段,標記階段和清除階段。一種可行的實現是,在標記階段通過根節(jié)點標記所有從根節(jié)點開始可達的對象,然后在清除節(jié)點清除所有未被標記的垃圾對象。標記-清除算法可能產生的最大問題就是空間碎片,因為回收后的空間是不連續(xù)的
復制算法(Copying):與標記-清除算法相比,復制算法是一種相對高效的回收方法。核心思想是將原有的內存空間分為兩塊,每次只使用一塊,在垃圾回收時,將正在使用的內存中的存活對象復制到未使用的內存中,之后清除正在使用的內存塊的所有對象,交換兩個內存的角色,完成垃圾回收。如果系統(tǒng)中垃圾對象多,復制算法需要復制的對象數量不會太多,又由于對象被復制到新的內存空間,所以確保沒有碎片。但是復制算法缺點是將系統(tǒng)內存折半,因此單純的復制算法難以讓人接受
在Java的新生代串行垃圾回收期中,使用了復制算法的思想,將新生代分為eden空間、from空間和to空間3個部分。其中from和to空間可以視為用于復制的兩塊大小相同、地位相等且可角色互換的空間塊。復制算法比較適合新生代,因為在新生代中,垃圾對象通常會多于存活對象,復制算法的效果會比較好
標記-壓縮算法(Mark-Compact):復制算法的高效建立在存活對象少、垃圾對象多的場景下,因此適合年輕代。但是對于老年代,更常見的是大部分對象都是存活對象,因此基于這種特性,需要使用新的算法。標記-壓縮算法在標記-清除的基礎上做了一些優(yōu)化。與標記-清除一樣從根節(jié)點開始對所有可達對象做一次標記,但是之后清理未標記對象時,將存活對象壓縮到內存的一端,然后清理邊界外的所有空間。這種方法既避免碎片的產生,又不需要兩塊相同的內存空間,因此性價比較高
增量算法(Incremental Collecting):對大部分垃圾回收算法來說,在回收過程應用程序都處于stop the world狀態(tài),所有線程都會掛起,暫停一切正常工作等待垃圾回收完成。如果回收時間很長就會嚴重影響用戶體驗和系統(tǒng)穩(wěn)定性。增量算法的基本思想是,如果一次性將所有垃圾進行處理,需要造成系統(tǒng)長時間停頓,那么就可以讓垃圾回收線程和應用程序線程交替執(zhí)行。每次垃圾收集線程只收集一小塊的內存空間,然后切換到應用程序線程,如此反復直到垃圾收集完成。這種方式下,能間斷性地執(zhí)行應用程序代碼,所以能減少系統(tǒng)停頓時間,但是由于線程切換和上下文轉換的消耗,會使得垃圾回收總成本上升,造成系統(tǒng)吞吐量下降
分代(Generational Collecting):所有的算法都無法完全替代其他算法,都具有獨特的優(yōu)勢和特點,因此根據垃圾回收對象的特性,使用合適的算法才是明智之舉。分代就是基于這種思想,將內存區(qū)間根據對象的特點分成幾塊,根據每塊內存的特點選擇不同的回收算法,提高回收效率。幾乎所有的垃圾回收器都區(qū)分年輕代和老年代
分區(qū)算法(Region):分代算法是按照對象的生命周期長短劃分成兩個部分,分區(qū)算法是將整個堆空間劃分為連續(xù)的不同小區(qū)間,每個小區(qū)間都獨立使用,獨立回收。這種算法的好處是可以控制一次回收多少個小區(qū)間,一般來說堆空間越大,相同條件下一次GC所需的時間就越長,那么每次合理回收若干個小區(qū)塊,從而可以減少一次GC所產生的停頓
分類
按線程數分,可以分為串行垃圾回收器和并行垃圾回收器;按工作模式分,可以分為并發(fā)式垃圾回收器和獨占式垃圾回收器;按碎片處理方式,可以分為壓縮式垃圾回收器和非壓縮式垃圾回收器;按工作的內存區(qū)間,可以分為新生代垃圾回收器和年老代垃圾回收器
評價指標:
1、吞吐量:指在應用程序的生命周期內,應用程序所花費的時間和系統(tǒng)總運行時間(應用耗時+GC耗時)的比值
2、垃圾回收器負載:與吞吐量相反,是垃圾回收器耗時與系統(tǒng)運行總時間的比值
3、停頓時間:指垃圾回收器正在運行時,應用程序的暫停時間
4、垃圾回收頻率:指垃圾回收器多長時間會運行一次
5、反應時間:指當一個對象成為垃圾后,多長時間它所占用的內存會被釋放
6、堆分配:不同垃圾回收器對堆內存的分配方式可能不同,一個良好的垃圾回收期需要有一個合理的堆內存區(qū)間劃分
新生代串行收集器 Serial
是最古老的一種,也是JDK最基本的垃圾收集器之一。主要有兩個特點:第一,它僅僅使用單線程進行垃圾回收;第二,它是獨占式的垃圾回收。因此垃圾收集器運行時,應用所有線程都停止工作進行等待。雖然如此,但是串行收集器是一個成熟、經過長時間考驗的極為高效的收集器。新生代串行收集器使用復制算法,實現相對簡單,邏輯處理高效,而且沒有線程切換的開銷,在諸如單CPU或較小應用內存等硬件的平臺,它的性能可以超過并行回收器和并發(fā)回收器
-XX:+UseSerialGC,指定使用新生代串行收集器和年老代串行收集器。當JVM在Client模式下時,它是默認的垃圾收集器
老年代串行收集器 Serial Old
采用的是標記-壓縮算法,和新生代串行收集器一樣,是串行的、獨占式的垃圾回收器。由于老年代垃圾回收器通常比新生代垃圾回收器使用更長的時間,因此在堆空間較大的應用中,一旦老年串行收集器啟動,應用程序將會因此停頓幾秒甚至更長。雖然如此,作為老牌的垃圾回收器,老年代串行收集器可以和多種新生代回收器配合使用,同時它也作為CMS回收器的備用回收器
-XX:+UseSerialGC,新生代、老年代都使用串行回收器
-XX:+UseParNewGC,新生代使用并行收集器,老年代使用串行收集器
-XX:+UseParallelGC,新生代使用并行回收收集器,老年代使用串行收集器
新生代并行收集器 ParNew
并行收集器是工作在新生代的垃圾收集器,它只是簡單地將串行回收器多線程化,它的回收策略、算法以及參數和串行回收器一樣,并行回收器也是獨占式的回收器。但由于多線程進行垃圾回收,因此在并發(fā)能力較強的CPU上,暫停時間短于串行回收器
-XX:ParallelGCThreads,指定工作時的線程數量,一般與CPU數量相當
-XX:+UseParNewGC,新生代使用并行收集器,老年代使用串行收集器
-XX:+UseConcMarkSweepGC,新生代使用并行收集器,老年代使用CMS
新生代并行回收收集器 Parallel Scavenge
新生代并行回收器也是使用復制算法的收集器,表面上和并行收集器一樣,都是多線程、獨占式的收集器。但是它有一個重要的特點,就是它非常關注系統(tǒng)的吞吐量。除此之外,并行回收收集器與并行收集器另一個不同之處在于,它還支持一種自適應的GC調節(jié)策略,這種模式下,新生代的大小、eden和survivor的比例、晉升老年代的對象年齡等參數都會被自動調整,已達到堆大小、吞吐量和停頓時間的平衡點,在手動調優(yōu)比較難的場合可以使用這種自適應的方式
-XX:+UseParallelGC,新生代使用并行回收收集器,老年代使用串行收集器
-XX:+UseParallelOldGC,新生代和老年代都使用并行回收收集器
-XX:MaxGCPauseMillis,設置最大垃圾收集停頓時間,值為大于0的整數。收集器在工作時,會調整Java堆大小或其他參數,盡可能把停頓時間控制在范圍內。如果把值設置過小,可能JVM會使用很小的堆(小堆回收更快),這將導致垃圾回收變得頻繁,反而增加總時間,降低吞吐量
-XX:GCTimeRatio,設置吞吐量大小,值是0~100之間的整數
-XX:+UseAdaptiveSizePolicy,打開自適應GC策略
老年代并行回收收集器 Parallel Old
老年代并行回收收集器也是一種多線程并發(fā)的收集器,和新生代并行回收收集器一樣,也是一種關注吞吐量的收集器。老年代并行回收收集器使用標記-壓縮算法
-XX:+UseParallelOldGC,新生代和老年代都使用并行回收收集器
CMS收集器
與并行回收收集器不同,CMS收集器主要關注系統(tǒng)停頓時間,是一種以獲取最短回收停頓時間為目標的收集器。是Concurrent Mark Sweep的縮寫,意為并發(fā)標記清除,因此它使用的是標記-清除算法,同時也是一個使用多線程并行回收的垃圾收集器。只會回收老年代和永久帶(1.8開始為元數據區(qū),需要設置CMSClassUnloadingEnabled),不會收集年輕代。CMS收集器的工作過程略顯復雜,主要步驟有:初始標記、并發(fā)標記、重新標記、并發(fā)清除和并發(fā)重置(回收完成后重新初始化CMS數據結構和數據)。其中初始標記和重新標記是獨占系統(tǒng)資源的,而并發(fā)標記、并發(fā)清除和并發(fā)重置是可以和用戶線程一起并發(fā)執(zhí)行的。因此不算是獨占式的,可以在應用程序運行過程中進行垃圾回收
-XX:+UseConcMarkSweepGC,使用CMS收集器
-XX:ParallelGCThreads,設置CMS的線程數量
-XX:CMSInitiatingOccupancyFraction,設置老年代使用率回收閾值,因為CMS回收時,應用持續(xù)工作,因此會有新的垃圾產生,而這些垃圾在CMS回收過程中無法清除,因此CMS回收過程中還需要保證有足夠的內存可用,這樣就不等待堆內存飽和再進行回收,而是當堆內存使用率達到某一閾值后就進行回收
-XX:UseCMSCompactAtFullCollection,開關使CMS在垃圾收集完成后,進行一次內存碎片整理,內存碎片整理不是并發(fā)進行的
-XX:CMSFullGCsBeforeCompaction,指定進行多少次CMS回收后進行一次內存壓縮
G1收集器
是目前最新的垃圾回收器,目標是作為一款服務端的垃圾收集器,因此在吞吐量和停頓控制上,預期要優(yōu)于CMS收集器。與CMS相比,G收集器是基于標記-壓縮算法的,因此不會產生空間碎片,G1收集器還可以進行非常精確的停頓控制。G1能充分利用CPU、多核環(huán)境下的硬件優(yōu)勢,使用多個CPU(CPU或者CPU核心)來縮短stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執(zhí)行的GC動作,G1收集器仍然可以通過并發(fā)的方式讓java程序繼續(xù)執(zhí)行。雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但是還是保留了分代的概念。降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度為M毫秒的時間片段內。G1的運行步驟有1、初始標記;2、并發(fā)標記;3、最終標記;4、篩選回收
-XX:+UseG1GC,啟用G1回收器
-XX:+UnlockExperimentalVMOptions,允許使用實驗性參數
-XX:GCPauseIntervalMillis,設置停頓間隔時間
-XX:MaxGCPauseMillis,設置最大垃圾收集停頓時間
8、常見調優(yōu)方法
將新對象預留在新生代:由于Full GC的成本要遠遠高于Minor GC,因此盡可能將對象分配在新生代是一項明智的做法。雖然大部分情況下,JVM會嘗試在eden區(qū)分配對象,但是由于空間緊張等問題,很可能不得不將部分年輕代對象提前先老年代壓縮。因此在JVM參數調優(yōu)時,可以為應用分配一個合理的新生代空間,最大限度避免新對象直接進入老年代的情況
-Xmn,-XX:NewRatio,-XX:SurvivorRatio
大對象進入老年代:大部分情況下將對象分配到新生代是合理的,但是對于大對象,很可能擾亂新生代GC,并破壞新生代原有的對象結構。因為嘗試在新生代分配大對象,可能導致空間不足,JVM不得不將新生代中的年輕對象移動到年老代。因為大對象占用空間大,所以可能需要移動大量小的年輕對象進入老年代,這對GC來說是相當不利的。但是如果大對象是個短命的對象,這種情況出現比較頻繁,那對GC也是一種災難,擾亂了分代內存回收的思想,因此應該盡可能避免使用短命的大對象
-XX:PretenureSizeThreshold
設置對象進入老年代的年齡:對象在eden區(qū)經過一次GC后還存活,就移到survivior區(qū)并年齡加1,直到達到閾值進入老年代。默認值是15,但不意味著必須要達到這個年齡才能進入老年代。實際上,對象實際進入老年代的年齡是虛擬機在運行時根據內存使用情況動態(tài)計算的,參數只是可以指定閾值年齡的最大值,即實際晉升年齡取閾值與動態(tài)計算年齡中的最小值
-XX:MaxTenuringThreshold
穩(wěn)定與震蕩堆大小:穩(wěn)定的堆大小對垃圾回收是有利的,獲得穩(wěn)定堆大小的方式就是將-Xms和-Xmx設為大小一致。這樣運行時堆大小恒定,穩(wěn)定的堆空間可以減少GC次數。但是不穩(wěn)定的堆也并不是毫無用處,穩(wěn)定的堆雖然減少GC次數,但是也可能增加每次GC的時間。當系統(tǒng)不需要大內存時,讓堆大小在一個區(qū)間中震蕩,壓縮堆空間,使GC應對一個較小的堆,可以加快單次GC速度
-XX:MinHeapFreeRatio,-XX:MaxHeapFreeRatio
吞吐量優(yōu)先案例:-Xms與-Xmx一致,使用-XX:+UseParallelGC新生代使用并行回收收集器并設置線程數,-XX:UseParallelOldGC老年代也使用并行回收收集器
使用大頁案例:-XX:LargePageSizeInBytes設置大頁的大小,使用大的內存分頁可以增強CPU的內存尋址能力,從而提高系統(tǒng)性能
降低停頓案例:首先考慮使用關注系統(tǒng)停頓時間的CMS回收器,其次考慮減少Full GC次數,應盡可能將對象預留在新生代
9、其他實用JVM參數
JIT編譯參數,JVM的JIT編譯器,可以在運行時將字節(jié)碼編譯為本地代碼,從而提高函數的執(zhí)行效率,JIT編譯會花費一定時間,但未來運行中這些時間會被賺回來
-XX:CompileThreshold,JIT編譯閾值,當函數調用次數超過該值,JIT就將字節(jié)碼編譯為本地機器碼。在Client模式默認1500,Server模式下默認10000 -XX:+CITime,打印JIT編譯的耗時 -XX:+PrintCompilation,打印JIT編譯的信息堆快照
-XX:+HeapDumpOnOutOfMemoryError,將當前的堆信息保存到文件中,對于排查問題是很有幫助的 -XX:HeapDumpPath,指定堆快照保存位置取得GC信息
-verbose:gc或-XX:+PrintGC,獲取簡要的GC信息 -XX:+PrintGCDetails,獲取詳細GC信息類和對象跟蹤
-XX:+TraceClassLoading,跟蹤類加載信息 -XX:+TraceClassUnloading,跟蹤類卸載信息 -verbose:class,同時打開類加載和類卸載信息控制GC
-XX:+DisableExplicitGC,禁止在程序中使用System.gc()觸發(fā)Full GC -Xnoclassgc,不需要回收類 -Xincgc,開啟后系統(tǒng)會進行增量式的GC,增量式GC使用特定算法讓GC線程與應用線程交叉執(zhí)行,從而減小停頓時間Solaris下線程控制
-XX:+UseBoundThreads,綁定所有用戶線程到內核線程,減少線程進饑餓狀態(tài)的次數 -XX:+UseLWPSynchronization,使用內核線程代替線程同步 -XX:+UseVMInterruptibleIO,允許運行時中斷線程使用大頁
-XX:+UseLargePages,啟用大頁 -XX:LargePageSizeInBytes,設置大頁的大小總結
以上是生活随笔為你收集整理的JVM调优笔记:认识JVM内存模型(jdk1.8)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 普罗米修斯笔记:初识Prometheus
- 下一篇: BigDecimal运算的工具类