Java 虛擬機有自己完善的硬件架構, 如處理器、堆棧、寄存器等,還具有相應的指令系統。JVM 屏蔽了與具體操作系統平臺相關的信息,使得 Java 程序只需生成在 Java 虛擬機上運行的目標代碼 (字節碼), 就可以在多種平臺上不加修改地運行。
線程共享區域為:
1、java堆
2、方法區
線程私有區域為:
3、JVM棧
4、本地方法棧
5、程序計數器
各區域作用:
1、java堆:
java堆是jvm內存管理中最大的一塊,線程共享。在jvm啟動的時候創建。此區域唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。但是隨著JIT編譯器(即時編譯器)的發展與逃逸分析技術的逐漸成熟,棧上分配、標量替換優化技術將會導致一些微妙變化(對象可能會分配到棧上),所以這種所有對象都分配在堆上也不是那么絕對的。
java堆細分為新生代和老年代,新生代又分為Eden空間、From Survivor空間、To Survivor空間,新生代中垃圾回收算法為復制算法,復制算法是先將內存分為連個部分,一部分用來放入對象,而另一部分暫時不用,當使用的一部分內存要進行垃圾回收的時候會將不需要回收的對象復制保存在另一個空間中,然后再對使用過的那部分區域進行垃圾回收,這樣雖然效率很高,但是很浪費空間,所以一般將新生代分為Eden空間和兩個Survivor空間,其大小在HotSpot虛擬機中默認比例為8:1:1,這樣在新生代中采用復制算法回收垃圾效率就很高了,具體回收過程是將Eden區域和From Survivor區域作為對象的存儲空間,當要進行垃圾回收的時候先將這兩個區域中不需要回收的對象復制保存在To Survivor區域中,然后再進行垃圾回收。另外有一點是當一個對象在Eden區域和From Survivor區域中存儲的時候發現內存不足,這時會進行內存分配擔保,就是將此對象直接存入在老年代中。
關于對象的分配:對象優先在Eden區域分配,大對象會直接進入老年代,長期存活的對象會進入老年代,這里的長期存活是根據新生代中的對象年齡閾值來定義的,對象剛分配到新生代的時候年齡為1,每進行一次GC對象的年齡會加1,HotSpot中默認的閾值是15,也就是說對象年齡達到15歲的時候會被分配到老年區,這個值是可以通過參數配置的。
在進行垃圾回收的時候新生代GC又叫minor GC,老年代GC可以設置內存容量達到百分比的多少的時候進行GC,老年代的GC又叫Full GC,minor GC時間短,頻率高,而Full GC時間長,頻率低。
2、方法區
方法區又被稱為永久區,線程共享,是用來存儲已被JVM加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。方法區為堆的一個邏輯部分,但是在JDK1.7的HotSpot中已經將方法區中的字符串常量池移出,部分資料顯示JDK1.8已經去除了方法區(不確定)。不過已經可以猜測此區域將會被本地內存逐步取代。
這個區域很少進行垃圾回收,回收目標主要是針對常量池的回收和對類型的卸載。
3、JVM棧
JVM棧是線程私有的,它的生命周期與線程相同。JVM棧描述的是java方法執行的內存模型,每個方法在執行的同時都會創建一個棧幀,用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
局部變量表中存放了編譯期可知的各種基本數據類型、對象的引用類型。局部變量表中需要的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
4、本地方法棧
本地方法棧和JVM棧非常相似,它們之間的區別不過是jvm棧是為執行java方法服務,而本地方法棧是為jvm使用到對的本地方法服務。HotSpot虛擬機中直接把本地方法棧和JVM棧合二為一了。
5、程序計數器
程序計數器是一塊較小的內存空間,線程私有。它可以看作是當前線程所執行的字節碼的行號指示器。在jvm的概念模型里,字節碼解釋器工作就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
如果線程正在執行的是一個java方法,這個計數器記錄的是正在執行的jvm字節碼指令的地址;如果正在執行的是本地方法,這個計數器值則為空。
總結:
在jvm劃分的內存區域中JVM棧和本地方法棧可能會拋出StackOverflowError異常和OutOfMemoryError異常。java堆和方法區可能會拋出OutOfMemoryError異常。程序計數器中沒有地方規定會拋出這兩個異常。
2.gc基礎
2.1垃圾收集算法
序號算法說明優/缺點
1引用計數法 (Reference Counting)對于一個對象 A,只要有任何一個對象引用了 A,則 A 的引用計數器就加 1。無法處理循環引用的情況。因此,在 Java 的垃圾回收器中沒有使用這種算法。
循環引用問題描述如下:有對象 A 和對象 B,對象 A 中含有對象 B 的引用,對象 B 中含有對象 A 的引用。此時,對象 A 和對象 B 的引用計數器都不為 0。但是在系統中卻不存在任何第 3 個對象引用了 A 或 B。也就是說,A 和 B 是應該被回收的垃圾對象,但由于垃圾對象間相互引用,從而使垃圾回收器無法識別,引起內存泄漏。
2標記-清除算法 (Mark-Sweep)將垃圾回收分為兩個階段:標記階段和清除階段。
一種可行的實現是,在標記階段首先通過根節點,標記所有從根節點開始的較大對象。因此,未被標記的對象就是未被引用的垃圾對象。然后,在清除階段,清除所有未被標記的對象。最大的問題是存在大量的空間碎片,因為回收后的空間是不連續的。在對象的堆空間分配過程中,尤其是大對象的內存分配,不連續的內存空間的工作效率要低于連續的空間。
3復制算法 (Copying)將現有的內存空間分為兩快,每次只使用其中一塊,在垃圾回收時將正在使用的內存中的存活對象復制到未被使用的內存塊中,之后,清除正在使用的內存塊中的所有對象,交換兩個內存的角色,完成垃圾回收。
如果系統中的垃圾對象很多,復制算法需要復制的存活對象數量并不會太大。因此在真正需要垃圾回收的時刻,復制算法的效率是很高的。又由于對象在垃圾回收過程中統一被復制到新的內存空間中,因此,可確保回收后的內存空間是沒有碎片的。該算法的缺點是將系統內存折半。
4標記-壓縮算法 (Mark-Compact)復制算法的高效性是建立在存活對象少、垃圾對象多的前提下的。這種情況在年輕代經常發生,但是在老年代更常見的情況是大部分對象都是存活對象。如果依然使用復制算法,由于存活的對象較多,復制的成本也將很高。
標記-壓縮算法是一種老年代的回收算法,它在標記-清除算法的基礎上做了一些優化。也首先需要從根節點開始對所有可達對象做一次標記,但之后,它并不簡單地清理未標記的對象,而是將所有的存活對象壓縮到內存的一端。之后,清理邊界外所有的空間。這種方法既避免了碎片的產生,又不需要兩塊相同的內存空間,因此,其性價比比較高。
5增量算法 (Incremental Collecting)在垃圾回收過程中,應用軟件將處于一種 CPU 消耗很高的狀態。在這種 CPU 消耗很高的狀態下,應用程序所有的線程都會掛起,暫停一切正常的工作,等待垃圾回收的完成。如果垃圾回收時間過長,應用程序會被掛起很久,將嚴重影響用戶體驗或者系統的穩定性。
增量算法的基本思想是,如果一次性將所有的垃圾進行處理,需要造成系統長時間的停頓,那么就可以讓垃圾收集線程和應用程序線程交替執行。每次,垃圾收集線程只收集一小片區域的內存空間,接著切換到應用程序線程。依次反復,直到垃圾收集完成。使用這種方式,由于在垃圾回收過程中,間斷性地還執行了應用程序代碼,所以能減少系統的停頓時間。但是,因為線程切換和上下文轉換的消耗,會使得垃圾回收的總體成本上升,造成系統吞吐量的下降。
2.2JVM 垃圾回收器分類
根據垃圾回收對象的特性,不同階段最優的方式是使用合適的算法用于本階段的垃圾回收。
從不同角度分析垃圾收集器,可以將其分為不同的類型。
1. 按線程數分,可以分為串行垃圾回收器和并行垃圾回收器。串行垃圾回收器一次只使用一個線程進行垃圾回收;并行垃圾回收器一次將開啟多個線程同時進行垃圾回收。在并行能力較強的 CPU 上,使用并行垃圾回收器可以縮短 GC 的停頓時間。
2. 按照工作模式分,可以分為并發式垃圾回收器和獨占式垃圾回收器。并發式垃圾回收器與應用程序線程交替工作,以盡可能減少應用程序的停頓時間;獨占式垃圾回收器 (Stop the world) 一旦運行,就停止應用程序中的其他所有線程,直到垃圾回收過程完全結束。
3. 按碎片處理方式可分為壓縮式垃圾回收器和非壓縮式垃圾回收器。壓縮式垃圾回收器會在回收完成后,對存活對象進行壓縮整理,消除回收后的碎片;非壓縮式的垃圾回收器不進行這步操作。
4. 按工作的內存區間,又可分為新生代垃圾回收器和老年代垃圾回收器。
可以用以下指標評價一個垃圾處理器的好壞。
吞吐量:指在應用程序的生命周期內,應用程序所花費的時間和系統總運行時間的比值。系統總運行時間=應用程序耗時+GC 耗時。如果系統運行了 100min,GC 耗時 1min,那么系統的吞吐量就是 (100-1)/100=99%。
垃圾回收器負載:和吞吐量相反,垃圾回收器負載指來記回收器耗時與系統運行總時間的比值。
停頓時間:指垃圾回收器正在運行時,應用程序的暫停時間。對于獨占回收器而言,停頓時間可能會比較長。使用并發的回收器時,由于垃圾回收器和應用程序交替運行,程序的停頓時間會變短,但是,由于其效率很可能不如獨占垃圾回收器,故系統的吞吐量可能會較低。
垃圾回收頻率:指垃圾回收器多長時間會運行一次。一般來說,對于固定的應用而言,垃圾回收器的頻率應該是越低越好。通常增大堆空間可以有效降低垃圾回收發生的頻率,但是可能會增加回收產生的停頓時間。
反應時間:指當一個對象被稱為垃圾后多長時間內,它所占據的內存空間會被釋放。
堆分配:不同的垃圾回收器對堆內存的分配方式可能是不同的。一個良好的垃圾收集器應該有一個合理的堆內存區間劃分。
垃圾收集器種類:
新生代串行收集器
串行收集器主要有兩個特點:第一,它僅僅使用單線程進行垃圾回收;第二,它獨占式的垃圾回收。
在 HotSpot 虛擬機中,使用-XX:+UseSerialGC參數可以指定使用新生代串行收集器和老年代串行收集器。當 JVM 在 Client 模式下運行時,它是默認的垃圾收集器。
老年代串行收集器
老年代串行收集器使用的是標記-壓縮算法。
可以嘗試使用以下參數:-XX:+UseSerialGC: 新生代、老年代都使用串行回收器。
如果使用-XX:+UseParNewGC 參數設置,表示新生代使用并行收集器,老年代使用串行收集器
如果使用-XX:+UseParallelGC 參數設置,表示新生代和老年代均使用并行回收收集器。
并行收集器
并行收集器是工作在新生代的垃圾收集器,它只簡單地將串行回收器多線程化。它的回收策略、算法以及參數和串行回收器一樣。
并行回收器也是獨占式的回收器,在收集過程中,應用程序會全部暫停。但由于并行回收器使用多線程進行垃圾回收,因此,在并發能力比較強的 CPU 上,它產生的停頓時間要短于串行回收器,而在單 CPU 或者并發能力較弱的系統中,并行回收器的效果不會比串行回收器好,由于多線程的壓力,它的實際表現很可能比串行回收器差。
設置參數-XX:+UseConcMarkSweepGC可以要求新生代使用并行收集器,老年代使用 CMS。
并行收集器工作時的線程數量可以使用-XX:ParallelGCThreads 參數指定。一般,最好與 CPU 數量相當,避免過多的線程數影響垃圾收集性能。在默認情況下,當 CPU 數量小于 8 個,ParallelGCThreads 的值等于 CPU 數量,大于 8 個,ParallelGCThreads 的值等于 3+[5*CPU_Count]/8]。
新生代并行回收 (Parallel Scavenge) 收集器
新生代并行回收收集器也是使用復制算法的收集器。從表面上看,它和并行收集器一樣都是多線程、獨占式的收集器。但是,并行回收收集器有一個重要的特點:它非常關注系統的吞吐量。
新生代并行回收收集器可以使用以下參數啟用:
-XX:+UseParallelGC:新生代使用并行回收收集器,老年代使用串行收集器。
-XX:+UseParallelOldGC:新生代和老年代都是用并行回收收集器。
-XX:+MaxGCPauseMills:設置最大垃圾收集停頓時間,它的值是一個大于 0 的整數。收集器在工作時會調整 Java 堆大小或者其他一些參數,盡可能地把停頓時間控制在 MaxGCPauseMills 以內。如果希望減少停頓時間,而把這個值設置得很小,為了達到預期的停頓時間,JVM 可能會使用一個較小的堆 (一個小堆比一個大堆回收快),而這將導致垃圾回收變得很頻繁,從而增加了垃圾回收總時間,降低了吞吐量。
-XX:+GCTimeRatio:設置吞吐量大小,它的值是一個 0-100 之間的整數。假設 GCTimeRatio 的值為 n,那么系統將花費不超過 1/(1+n) 的時間用于垃圾收集。比如 GCTimeRatio 等于 19,則系統用于垃圾收集的時間不超過 1/(1+19)=5%。默認情況下,它的取值是 99,即不超過 1%的時間用于垃圾收集。
除此之外,并行回收收集器與并行收集器另一個不同之處在于,它支持一種自適應的 GC 調節策略,使用-XX:+UseAdaptiveSizePolicy 可以打開自適應 GC 策略。在這種模式下,新生代的大小、eden 和 survivor 的比例、晉升老年代的對象年齡等參數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。在手工調優比較困難的場合,可以直接使用這種自適應的方式,僅指定虛擬機的最大堆、目標的吞吐量 (GCTimeRatio) 和停頓時間 (MaxGCPauseMills),讓虛擬機自己完成調優工作。
老年代并行回收收集器
和新生代并行回收收集器一樣,它也是一種關注吞吐量的收集器。老年代并行回收收集器使用標記-壓縮算法,JDK1.6 之后開始啟用、
CMS 收集器
與并行回收收集器不同,CMS 收集器主要關注于系統停頓時間。CMS 是 Concurrent Mark Sweep 的縮寫,意為并發標記清除,從名稱上可以得知,它使用的是標記-清除算法,同時它又是一個使用多線程并發回收的垃圾收集器。
CMS 工作時,主要步驟有:初始標記、并發標記、重新標記、并發清除和并發重置。其中初始標記和重新標記是獨占系統資源的,而并發標記、并發清除和并發重置是可以和用戶線程一起執行的。因此,從整體上來說,CMS 收集不是獨占式的,它可以在應用程序運行過程中進行垃圾回收。
CMS 默認啟動的線程數是 (ParallelGCThreads+3)/4),ParallelGCThreads 是新生代并行收集器的線程數
CMS 收集器不會等待堆內存飽和時才進行垃圾回收,而是當前堆內存使用率達到某一閾值時,便開始進行回收,以確保應用程序在 CMS 工作過程中依然有足夠的空間支持應用程序運行。
這個回收閾值可以使用-XX:CMSInitiatingOccupancyFraction來指定,默認是 68。即當老年代的空間使用率達到 68%時,會執行一次 CMS 回收。如果應用程序的內存使用率增長很快,在 CMS 的執行過程中,已經出現了內存不足的情況,此時,CMS 回收將會失敗,JVM 將啟動老年代串行收集器進行垃圾回收。
G1 收集器 (Garbage First)
G1 收集器的目標是作為一款服務器的垃圾收集器,因此,它在吞吐量和停頓控制上,預期要優于 CMS 收集器。
與 CMS 收集器相比,G1 收集器是基于標記-壓縮算法的。因此,它不會產生空間碎片,也沒有必要在收集完成后,進行一次獨占式的碎片整理工作。G1 收集器還可以進行非常精確的停頓控制。它可以讓開發人員指定當停頓時長為 M 時,垃圾回收時間不超過 N。使用參數-XX:+UnlockExperimentalVMOptions –XX:+UseG1GC 來啟用 G1 回收器,設置 G1 回收器的目標停頓時間:-XX:MaxGCPauseMills=20,-XX:GCPauseIntervalMills=200。
3.虛擬機&GC 相關參數總結
虛擬機提供了-XX:+PrintGCDetails參數打印收集器日志,并且在進程退出時輸出當前內存各區域的分配情況。
例如 通過?-Xms20M?-Xmx20M?-Xmn10M這3個參數限制java堆大小為20MB,且不可擴展。其中10MB分配給新生代,剩下的10MB分配給老年代。
-XX:SurvivorRatio=8?決定了新生代中Eden區與一個Survivor區的比例為8:1,即Eden區=8MB,一個Survivor=1MB,另一個Surviveor也為1MB。
1. 與串行回收器相關的參數
-XX:+UseSerialGC:在新生代和老年代使用串行回收器。
-XX:+SuivivorRatio:設置 eden 區大小和 survivor 區大小的比例。
-XX:+PretenureSizeThreshold:設置大對象直接進入老年代的閾值。當對象的大小超過這個值時,將直接在老年代分配。
-XX:MaxTenuringThreshold:設置對象進入老年代的年齡的最大值。每一次 Minor GC 后,對象年齡就加 1。任何大于這個年齡的對象,一定會進入老年代。
2. 與并行 GC 相關的參數
-XX:+UseParNewGC: 在新生代使用并行收集器。
-XX:+UseParallelOldGC: 老年代使用并行回收收集器。
-XX:ParallelGCThreads:設置用于垃圾回收的線程數。通常情況下可以和 CPU 數量相等。但在 CPU 數量比較多的情況下,設置相對較小的數值也是合理的。
-XX:MaxGCPauseMills:設置最大垃圾收集停頓時間。它的值是一個大于 0 的整數。收集器在工作時,會調整 Java 堆大小或者其他一些參數,盡可能地把停頓時間控制在 MaxGCPauseMills 以內。
-XX:GCTimeRatio:設置吞吐量大小,它的值是一個 0-100 之間的整數。假設 GCTimeRatio 的值為 n,那么系統將花費不超過 1/(1+n) 的時間用于垃圾收集。
-XX:+UseAdaptiveSizePolicy:打開自適應 GC 策略。在這種模式下,新生代的大小,eden 和 survivor 的比例、晉升老年代的對象年齡等參數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。
3. 與 CMS 回收器相關的參數
-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。
-XX:+ParallelCMSThreads: 設定 CMS 的線程數量。
-XX:+CMSInitiatingOccupancyFraction:設置 CMS 收集器在老年代空間被使用多少后觸發,默認為 68%。
-XX:+UseFullGCsBeforeCompaction:設定進行多少次 CMS 垃圾回收后,進行一次內存壓縮。
-XX:+CMSClassUnloadingEnabled:允許對類元數據進行回收。
-XX:+CMSParallelRemarkEndable:啟用并行重標記。
-XX:CMSInitatingPermOccupancyFraction:當永久區占用率達到這一百分比后,啟動 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。
-XX:UseCMSInitatingOccupancyOnly:表示只在到達閾值的時候,才進行 CMS 回收。
-XX:+CMSIncrementalMode:使用增量模式,比較適合單 CPU。
4. 與 G1 回收器相關的參數
-XX:+UseG1GC:使用 G1 回收器。
-XX:+UnlockExperimentalVMOptions:允許使用實驗性參數。
-XX:+MaxGCPauseMills:設置最大垃圾收集停頓時間。
-XX:+GCPauseIntervalMills:設置停頓間隔時間。
5. 其他參數
-XX:+DisableExplicitGC: 禁用顯示 GC。
4.優化經驗總結
如何將新對象預留在年輕代
由于 Full GC 的成本遠遠高于 Minor GC,因此某些情況下需要盡可能將對象分配在年輕代。
雖然在大部分情況下,JVM 會嘗試在 Eden 區分配對象,但是由于空間緊張等問題,很可能不得不將部分年輕對象提前向年老代壓縮。因此,在 JVM 參數調優時可以為應用程序分配一個合理的年輕代空間,以最大限度避免新對象直接進入年老代的情況發生。
a.-Xms20M-Xmx20M-Xmn10M,適當增大Xmn大小:-XX:+PrintGCDetails -Xmx20M -Xms20M-Xmn6M
b.一般來說,Survivor 區的空間不夠,或者占用量達到 50%時,就會使對象進入年老代 (不管它的年齡有多大);-XX:+PrintGCDetails -Xmx1000M -Xms500M -Xmn100M -XX:SurvivorRatio=8
c.可以嘗試加上-XX:TargetSurvivorRatio=90 參數,這樣可以提高 from 區的利用率,使 from 區使用到 90%時,再將對象送入年老代
如何讓大對象進入年老代
嘗試在年輕代分配大對象,很可能導致空間不足,為了有足夠的空間容納大對象,JVM 不得不將年輕代中的年輕對象挪到年老代。因為大對象占用空間多,所以可能需要移動大量小的年輕對象進入年老代,這對 GC 相當不利。
使用參數-XX:PetenureSizeThreshold 設置大對象直接進入年老代的閾值。當對象的大小超過這個值時,將直接在年老代分配。參數-XX:PetenureSizeThreshold 只對串行收集器和年輕代并行收集器有效,并行回收收集器不識別這個參數。
如果需要將 1MB 以上的對象直接在年老代分配,設置-XX:PetenureSizeThreshold=1000000
如何設置對象進入年老代的年齡
如果對象在 Eden 區,經過一次 GC 后依然存活,則被移動到 Survivor 區中,對象年齡加 1。以后,如果對象每經過一次 GC 依然存活,則年齡再加 1。當對象年齡達到閾值時,就移入年老代。
閾值的最大值可以通過參數-XX:MaxTenuringThreshold 來設置,默認值是 15。
實際晉升年老代年齡等于虛擬機在運行時根據內存使用情況動態計算所得的年齡與-XX:MaxTenuringThreshold 中較小的那個。
穩定的 Java 堆 VS 動蕩的 Java 堆
獲得一個穩定的堆大小的方法是使-Xms 和-Xmx 的大小一致,即最大堆和最小堆 (初始堆) 一樣.
系統在運行時堆大小理論上是恒定的,穩定的堆空間可以減少 GC 的次數。
一個不穩定的堆并非毫無用處。穩定的堆大小雖然可以減少 GC 次數,但同時也增加了每次 GC 的時間。讓堆大小在一個區間中震蕩,在系統不需要使用大內存時,壓縮堆空間,使 GC 應對一個較小的堆,可以加快單次 GC 的速度。
-XX:MinHeapFreeRatio 參數用來設置堆空間最小空閑比例,默認值是 40。當堆空間的空閑內存小于這個數值時,JVM 便會擴展堆空間。
-XX:MaxHeapFreeRatio 參數用來設置堆空間最大空閑比例,默認值是 70。當堆空間的空閑內存大于這個數值時,便會壓縮堆空間,得到一個較小的堆。
當-Xmx 和-Xms 相等時,-XX:MinHeapFreeRatio 和-XX:MaxHeapFreeRatio 兩個參數無效。
在一個穩定的堆中,堆空間大小始終不變,每次 GC 時,都要應對一個 40MB 的空間。因此,雖然 GC 次數減小了,但是單次 GC 速度不如一個震蕩的堆。
增大吞吐量提升系統性能
吞吐量優先的方案將會盡可能減少系統執行垃圾回收的總時間,故可以考慮關注系統吞吐量的并行回收收集器。
–Xmx380m –Xms3800m:設置 Java 堆的最大值和初始值。一般情況下,為了避免堆內存的頻繁震蕩,導致系統性能下降,我們的做法是設置最大堆等于最小堆。假設這里把最小堆減少為最大堆的一半,即 1900m,那么 JVM 會盡可能在 1900MB 堆空間中運行,如果這樣,發生 GC 的可能性就會比較高;
-Xss128k:減少線程棧的大小,這樣可以使剩余的系統內存支持更多的線程;
-Xmn2g:設置年輕代區域大小為 2GB;
–XX:+UseParallelGC:年輕代使用并行垃圾回收收集器。這是一個關注吞吐量的收集器,可以盡可能地減少 GC 時間。
–XX:ParallelGC-Threads:設置用于垃圾回收的線程數,通常情況下,可以設置和 CPU 數量相等。但在 CPU 數量比較多的情況下,設置相對較小的數值也是合理的;
–XX:+UseParallelOldGC:設置年老代使用并行回收收集器。
使用非占有的垃圾回收器
為降低應用軟件的垃圾回收時的停頓,首先考慮的是使用關注系統停頓的 CMS 回收器,其次,為了減少 Full GC 次數,應盡可能將對象預留在年輕代,因為年輕代 Minor GC 的成本遠遠小于年老代的 Full GC。
–XX:ParallelGCThreads=20:設置 20 個線程進行垃圾回收;
–XX:+UseParNewGC:年輕代使用并行回收器;
–XX:+UseConcMarkSweepGC:年老代使用 CMS 收集器降低停頓;
–XX:+SurvivorRatio:設置 Eden 區和 Survivor 區的比例為 8:1。稍大的 Survivor 空間可以提高在年輕代回收生命周期較短的對象的可能性,如果 Survivor 不夠大,一些短命的對象可能直接進入年老代,這對系統來說是不利的。
–XX:TargetSurvivorRatio=90:設置 Survivor 區的可使用率。這里設置為 90%,則允許 90%的 Survivor 空間被使用。默認值是 50%。故該設置提高了 Survivor 區的使用率。當存放的對象超過這個百分比,則對象會向年老代壓縮。因此,這個選項更有助于將對象留在年輕代。
–XX:MaxTenuringThreshold:設置年輕對象晉升到年老代的年齡。默認值是 15 次,即對象經過 15 次 Minor GC 依然存活,則進入年老代。這里設置為 31,目的是讓對象盡可能地保存在年輕代區域。
4 參數解析
filterapi項目jetty啟動參數
JVM_ARGS="-server -Dapp.key=com.sankuai.web.meishifilter.filterapi -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djava.io.tmpdir=/tmp -Djava.net.preferIPv6Addresses=false"
JVM_HEAP="-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+ExplicitGCInvokesConcurrent -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSClassUnloadingEnabled -XX:+TieredCompilation -XX:CICompilerCount=4"
JVM_GC="-XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps"
JVM_SIZE="-Xmx4096m -Xms4096m -Xmn1536m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M"
序號JVM_HEAP
參數說明
1-XX:+UseConcMarkSweepGC新生代使用并行收集器,老年代使用CMS,使用CMS內存收集
最新的JVM版本,當使用-XX:+UseConcMarkSweepGC時,-XX:UseParNewGC會自動開啟,即年輕代使用多線程并行執行垃圾回收。
2-XX:+CMSParallelRemarkEnabledCMS參數,降低標記停頓
3-XX:+ExplicitGCInvokesConcurrent系統大量使用NIO中的DirectByteBuffer時,需要定期清理本地內存。
DirectByteBuffer通過內存映射,使Java進程直接訪問與文件相關聯的虛擬地址空間,減少了文件拷貝帶來的開銷,提高了文件讀取效率。這一塊虛擬地址空間并不是分配在jvm堆上,而是分配在native堆上。yong gc不能回收這部分空間,只能通過Full gc順帶進行回收,那是因為Full gc時會觸發sun.misc.Cleaner,對DirectByteBuffer對象做清理工作。
為了減少fullGC的卡頓現象,有以下幾種解決辦法:
當程序中出現:
使用了NIO或者NIO框架(Mina/Netty等)
使用了DirectByteBuffer分配字節緩沖區
使用了MappedByteBuffer做內存映射
最好使用ExplicitGCInvokesConcurrent參數來替代DisableExplicitGC。需要注意的是ExplicitGCInvokesConcurrent只能配合CMS收集器使用。
關于堆外內存參考堆外內存
參考http://blog.csdn.net/aesop_wubo/article/details/38406709
4-XX:CMSInitiatingOccupancyFraction=70CMS 收集器不會等待堆內存飽和時才進行垃圾回收,而是當前堆內存使用率達到某一閾值時,便開始進行回收,以確保應用程序在 CMS 工作過程中依然有足夠的空間支持應用程序運行。
這個回收閾值可以使用-XX:CMSInitiatingOccupancyFraction 來指定,默認是 68。即當老年代的空間使用率達到 68%時,會執行一次 CMS 回收。如果應用程序的內存使用率增長很快,在 CMS 的執行過程中,已經出現了內存不足的情況,此時,CMS 回收將會失敗,JVM 將啟動老年代串行收集器進行垃圾回收。
5-XX:+UseCMSInitiatingOccupancyOnly我們用-XX+UseCMSInitiatingOccupancyOnly標志來命令JVM不基于運行時收集的數據來啟動CMS垃圾收集周期。而是,當該標志被開啟時,JVM通過CMSInitiatingOccupancyFraction的值進行每一次CMS收集,而不僅僅是第一次。然而,請記住大多數情況下,JVM比我們自己能作出更好的垃圾收集決策。因此,只有當我們充足的理由(比如測試)并且對應用程序產生的對象的生命周期有深刻的認知時,才應該使用該標志。
6XX:+CMSPermGenSweepingEnabled相對于并行收集器,CMS收集器默認不會對永久代進行垃圾回收。如果希望對永久代進行垃圾回收,可用設置標志-XX:+CMSClassUnloadingEnabled。在早期JVM版本中,要求設置額外的標志-XX:+CMSPermGenSweepingEnabled。注意,即使沒有設置這個標志,一旦永久代耗盡空間也會嘗試進行垃圾回收,但是收集不會是并行的,而再一次進行Full GC。
7-XX:+TieredCompilation打開“多層編譯”(tiered compilation)
參考分層編譯 。
在該模式下,代碼會先被解釋器執行,積累到足夠熱度的時候由client compiler(C1)編譯,然后繼續積累熱度到一定程度會進一步被server compiler(C2)重新以更高的優化程度編譯。
Oracle JDK從JDK 6u25以后的版本支持了多層編譯(-XX:+TieredCompilation),這個的好處是之前server都是采用c2高級編譯的,會比較耗時且要運行一段時間才會觸發編譯,而c1編譯是比較輕量的也比較快觸發,因此在啟用了多層編譯后,可以在啟動后更快的讓部分代碼先進入編譯模式。(啟動使用client模式,啟動后使用server模式)
關于“java啟動時的速度”參考http://hellojava.info/?tag=tieredcompilation
8-XX:CICompilerCount=4編譯線程數,server模式默認為2
序號方法備注
1禁用Full gc顯示調用(-XX:+DisableExplicitGC)容易導致OOM:”如果啟用了-XX:+DisableExplicitGC選項,在以下情況下會出現OOM:系統各方面性能良好,無Full gc且DirectByteBuffer所占用的空間大于-Xmx分配的空間。因為DirectByteBuffer會不斷地在native堆分配空間,它的引用進入了old區,old區保存大量的引用,而不能被回收,最終會導致native堆空間不足。
減少Full gc次數通過sun.rmi.dgc.server.gcInterval和un.rmi.dgc.client.gcInterval選項調整Full gc的時間間隔進而減少Full gc次數,
例如可以使用-Dsun.rmi.dgc.server.gcInterval=7200000與-Dsun.rmi.dgc.client.gcInterval=7200000 選項控制Full gc時間間隔
降低每次Full gc的時間通過ExplicitGCInvokesConcurrent選項,可以使用CMS收集器來觸發Full gc(相比Full gc,CMS收集器會花費更多的時間,如果對QPS比較敏感的應用應降低CMS觸發的次數)
JVM_GC參數說明
1-XX:+PrintGCDetails打印收集器日志,并且在進程退出時輸出當前內存各區域的分配情況。
2-XX:+PrintHeapAtGC在進行GC的前后打印出堆的信息
3-XX:+PrintTenuringDistribution-XX:MaxTenuringThreshold= 設置熬過年輕代多少次收集后移入老人區,CMS中默認為0,熬過第一次GC就轉入,可以用-XX:+PrintTenuringDistribution 查看。
4-XX:+PrintGCTimeStamps輸出GC的時間戳(以基準時間的形式)
5-XX:+PrintGCDateStamps輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
JVM_SIZE
JVM_SIZE
1-Xmx4096mJVM最大可用內存
2-Xms4096mJVM最小可用內存
3-Xmn1536m年輕代
4-XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256MMetaspace(元空間)
移除永久代的工作從JDK1.7就開始了。JDK1.7中,存儲在永久代的部分數據就已經轉移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并沒完全移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變量(class statics)轉移到了java heap。
元空間的本質和永久代類似,都是對JVM規范中方法區的實現。不過元空間與永久代之間最大的區別在于:元空間并不在虛擬機中,而是使用本地內存。
-XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行類型卸載,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當提高該值。
-XX:MaxMetaspaceSize,最大空間,默認是沒有限制的。
除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:
-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空間容量的百分比,減少為分配空間所導致的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導致的垃圾收集
參考http://www.cnblogs.com/paddix/p/5309550.html
參考:
CMS參數http://ifeve.com/useful-jvm-flags-part-7-cms-collector/