JVM調優

1 JVM 簡單結構圖

 

 



 

1.1 類加載子系統與方法區:

類加載子系統負責從文件系統或者網絡中加載 Class 信息,加載的類信息存放於一塊稱爲方法區的內存空間。除了類的信息外,方法區中可能還會存放運行時常量池信息,包括字符串字面量和數字常量(這部分常量信息是 Class 文件中常量池部分的內存映射)。

1.2 Java 堆

java 堆在虛擬機啓動的時候建立,它是 java 程序最主要的內存工作區域。幾乎所有的 java 對象實例都存放在 java 堆中。堆空間是所有線程共享的,這是一塊與 java 應用密切相關的內存空間。

1.3 直接內存

java NIO 庫允許 java 程序使用直接內存。直接內存是在 java 堆外的、直接向系統申請的內存空間。通常訪問直接內存的速度會優於 java 堆。因此出於性能的考慮,讀寫頻繁的場合可能會考慮使用直接內存。由於直接內存在 java 堆外,因此它的大小不會直接受限於 Xmx 指定的最大堆大小,但是系統內存是有限的,java 堆和直接內存的總和依然受限於操作系統能給出的最大內存。

1.4 垃圾回收系統

垃圾回收系統是 java 虛擬機的重要組成部分,垃圾回收器可以對方法區、java 堆和直接內存進行回收。其中,java 堆是垃圾收集器的工作重點。和 C/C++不同,java 中所有的對象空間釋放都是隱式的,也就是說,java 中沒有類似 free()或者 delete()這樣的函數釋放指定的內存區域。對於不再使用的垃圾對象,垃圾回收系統會在後臺默默工作,默默查找、標識並釋放垃圾對象,完成包括 java 堆、方法區和直接內存中的全自動化管理。

1.5 Java 棧

每一個 java 虛擬機線程都有一個私有的 java 棧,一個線程的 java 棧在線程創建的時候被創建,java 棧中保存着幀信息,java 棧中保存着局部變量、方法參數,同時和 java 方法的調用、返回密切相關。

1.6 本地方法棧

本地方法棧和 java 棧非常類似,最大的不同在於 java 棧用於方法的調用,而本地方法棧則用於本地方法的調用,作爲對 java 虛擬機的重要擴展,java 虛擬機允許 java 直接調用本地方法(通常使用 C 編寫)

1.7 PC 寄存器

PCProgram Counter)寄存器也是每一個線程私有的空間,java 虛擬機會爲每一個 java 線程創建 PC 寄存器。在任意時刻,一個 java 線程總是在執行一個方法,這個正在被執行的方法稱爲當前方法。如果當前方法不是本地方法,PC 寄存器就會指向當前正在被執行的指令。如果當前方法是本地方法,那麼 PC 寄存器的值就是 undefined

1.8 執行引擎

執行引擎是 java 虛擬機的最核心組件之一,它負責執行虛擬機的字節碼,現代虛擬機爲了提高執行效率,會使用即時編譯(just in time)技術將方法編譯成機器碼後再執行。

Java HotSpot Client VM(-client),爲在客戶端環境中減少啓動時間而優化的執行引擎;本地應用開發使用。(如:eclipse

Java HotSpot Server VM(-server),爲在服務器環境中最大化程序執行速度而設計的執行引擎。應用在服務端程序。(如:tomcat

Java HotSpot Client 模式和 Server 模式的區別

當虛擬機運行在-client 模式的時候,使用的是一個代號爲 C1 的輕量級編譯器, -server 模式啓動的虛擬機採用相對重量級,代號爲 C2 的編譯器. C2 C1 編譯器編譯的相對徹底,服務起來之後,性能更高

JDK 安裝目錄/jre/lib/x86i386amd32amd64/jvm.cfg

文件中的內容,-server -client 哪一個配置在上,執行引擎就是哪一個。如果是 JDK1.5 版本且是 64 位系統應用時,-client 無效。

--64 位系統內容

-server KNOWN

-client IGNORE

--32 位系統內容

-server KNOWN

-client KNOWN

注意:在部分JDK1.6 版本和後續的JDK 版本(64 位系統)中,-client 參數已經不起作用

了,Server 模式成爲唯一

2 堆結構及對象分代

2.1 什麼是分代,分代的必要性是什麼

Java 虛擬機根據對象存活的週期不同,把堆內存劃分爲幾塊,一般分爲新生代、老年代和永久代(對 HotSpot 虛擬機而言),這就是 JVM 的內存分代策略。

堆內存是虛擬機管理的內存中最大的一塊,也是垃圾回收最頻繁的一塊區域,我們程序所有的對象實例都存放在堆內存中。給堆內存分代是爲了提高對象內存分配和垃圾回收的效率。試想一下,如果堆內存沒有區域劃分,所有的新創建的對象和生命週期很長的對象放在一起,隨着程序的執行,堆內存需要頻繁進行垃圾收集,而每次回收都要遍歷所有的對象,遍歷這些對象所花費的時間代價是巨大的,會嚴重影響我們的 GC 效率。

有了內存分代,情況就不同了,新創建的對象會在新生代中分配內存,經過多次回收仍然存活下來的對象存放在老年代中,靜態屬性、類信息等存放在永久代中,新生代中的對象存活時間短,只需要在新生代區域中頻繁進行 GC,老年代中對象生命週期長,內存回收的頻率相對較低,不需要頻繁進行回收,永久代中回收效果太差,一般不進行垃圾回收,還可以根據不同年代的特點採用合適的垃圾收集算法。分代收集大大提升了收集效率,這些都是內存分代帶來的好處。

2.2 分代的劃分

Java 虛擬機將堆內存劃分爲新生代、老年代和永久代,永久代是 HotSpot 虛擬機特有的概念(JDK1.8 之後爲 metaspace 替代永久代),它採用永久代的方式來實現方法區,其他的虛擬機實現沒有這一概念,而且 HotSpot 也有取消永久代的趨勢,在 JDK 1.7 HotSpot 已經開始了"去永久化",把原本放在永久代的字符串常量池移出。永久代主要存放常量、類信息、靜態變量等數據,與垃圾回收關係不大,新生代和老年代是垃圾回收的主要區域。

內存簡圖如下:

 



 

2.2.1 新生代(Young Generation

新生成的對象優先存放在新生代中,新生代對象朝生夕死,存活率很低,在新生代中,常規應用進行一次垃圾收集一般可以回收 70% ~ 95% 的空間,回收效率很高。

HotSpot 將新生代劃分爲三塊,一塊較大的 Eden(伊甸)空間和兩塊較小的 Survivor(倖存者)空間,默認比例爲 811。劃分的目的是因爲 HotSpot 採用複製算法來回收新生代,設置這個比例是爲了充分利用內存空間,減少浪費。新生成的對象在 Eden 區分配(大對象除外,大對象直接進入老年代),當 Eden 區沒有足夠的空間進行分配時,虛擬機將發起一次

Minor GC

GC 開始時,對象只會存在於 Eden 區和 From Survivor 區,To Survivor 區是空的(作爲保留區域)。GC 進行時,Eden 區中所有存活的對象都會被複制到 To Survivor 區,而在 From Survivor 區中,仍存活的對象會根據它們的年齡值決定去向,年齡值達到年齡閥值(默認爲 15,新生代中的對象每熬過一輪垃圾回收,年齡值就加 1GC 分代年齡存儲在對象的 header 中)的對象會被移到老年代中,沒有達到閥值的對象會被複制到 To Survivor 區。接着清空 Eden 區和 From Survivor 區,新生代中存活的對象都在 To Survivor 區。接着, From Survivor 區和 To Survivor 區會交換它們的角色,也就是新的 To Survivor 區就是上次 GC 清空的 From Survivor 區,新的 From Survivor 區就是上次 GC To Survivor 區,總之,不管怎樣都會保證 To Survivor 區在一輪 GC 後是空的。GC 時當 To Survivor 區沒有足夠的空間存放上一次新生代收集下來的存活對象時,需要依賴老年代進行分配擔保,將這些對象存放在老年代中。

2.2.2 老年代(Old Generationn

在新生代中經歷了多次(具體看虛擬機配置的閥值)GC 後仍然存活下來的對象會進入老年代中。老年代中的對象生命週期較長,存活率比較高,在老年代中進行 GC 的頻率相對而言較低,而且回收的速度也比較慢。

2.2.3 永久代(Permanent Generationn

永久代存儲類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據,對這一區域而

言,Java 虛擬機規範指出可以不進行垃圾收集,一般而言不會進行垃圾回收。

3 垃圾回收算法及分代垃圾收集器

3.1 垃圾收集器的分類

3.1.1 次收集器

Scavenge GC,指發生在新生代的 GC,因爲新生代的 Java 對象大多都是朝生夕死,所以 Scavenge GC 非常頻繁,一般回收速度也比較快。當 Eden 空間不足以爲對象分配內存時,會觸發 Scavenge GC

一般情況下,當新對象生成,並且在 Eden 申請空間失敗時,就會觸發 Scavenge GC,對 Eden 區域進行 GC,清除非存活對象,並且把尚且存活的對象移動到 Survivor 區。然後整理 Survivor 的兩個區。這種方式的 GC 是對年輕代的 Eden 區進行,不會影響到年老代。因爲大部分對象都是從 Eden 區開始的,同時 Eden 區不會分配的很大,所以 Eden 區的 GC 會頻繁進行。因而,一般在這裏需要使用速度快、效率高的算法,使 Eden 去能儘快空閒出來。

當年輕代堆空間緊張時會被觸發

相對於全收集而言,收集間隔較短

3.1.2 全收集器

Full GC,指發生在老年代的 GC,出現了 Full GC 一般會伴隨着至少一次的 Minor GC(老年代的對象大部分是 Scavenge GC 過程中從新生代進入老年代),比如:分配擔保失敗。Full GC 的速度一般會比 Scavenge GC 10 倍以上。當老年代內存不足或者顯式調用 System.gc() 方法時,會觸發 Full GC

當老年代或者持久代堆空間滿了,會觸發全收集操作可以使用 System.gc()方法來顯式的啓動全收集

全收集一般根據堆大小的不同,需要的時間不盡相同,但一般會比較長。

3.1.3 垃圾回收器的常規匹配

 



 

 

3.2 常見垃圾回收算法

3.2.1 引用計數(Reference Counting

比較古老的回收算法。原理是此對象有一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只用收集計數爲 0 的對象。此算法最致命的是無法處理循環引用的問題。

3.2.2 複製(Copying

此算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另外一個區域中。此算法每次只處理正在使用中的對象,因此複製成本比較小,同時複製過去以後還能進行相應的內存整理,不會出現"碎片"問題。當然,此算法的缺點也是很明顯的,就是需要兩倍內存空間。簡圖如下:

 



 

3.2.3 標記-清除(Mark-Sweep

此算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除。此算法需要暫停整個應用,同時,會產生內存碎片。簡圖如下:

 


 

3.2.4 標記-整理(Mark-Compact

此算法結合了"標記-清除"和"複製"兩個算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍歷整個堆,把清除未標記對象並且把存活對象 "壓縮"到堆的其中一塊,按順序排放。此算法避免了"標記-清除"的碎片問題,同時也避免了"複製"算法的空間問題。簡圖如下:

 



 

3.3 分代垃圾收集器

3.3.1 串行收集器(Serial

Serial 收集器是 Hotspot 運行在 Client 模式下的默認新生代收集器, 它的特點是:只用一個 CPU(計算核心)/一條收集線程去完成 GC 工作, 且在進行垃圾收集時必須暫停其他所有

的工作線程("Stop The World" -後面簡稱 STW)。可以使用-XX:+UseSerialGC 打開。

雖然是單線程收集, 但它卻簡單而高效, VM 管理內存不大的情況下(收集幾十 M~一兩百 M 的新生代), 停頓時間完全可以控制在幾十毫秒~一百多毫秒內。

 



 

3.3.2 並行收集器(ParNew

ParNew 收集器其實是前面 Serial 的多線程版本, 除使用多條線程進行 GC , 包括 Serial 可用的所有控制參數、收集算法、STW、對象分配規則、回收策略等都與 Serial 完全一樣(也是 VM 啓用 CMS 收集器-XX: +UseConcMarkSweepGC 的默認新生代收集器)。由於存在線程切換的開銷, ParNew 在單 CPU 的環境中比不上 Serial, 且在通過超線程技術實現的兩個 CPU 的環境中也不能 100%保證能超越 Serial. 但隨着可用的 CPU 數量的增加, 收集效率肯定也會大大增加(ParNew 收集線程數與 CPU 的數量相同, 因此在 CPU 數量過大的環境中, 可用-XX:ParallelGCThreads=<N>參數控制 GC 線程數)

 


 

3.3.3 Parallel Scavenge 收集器

ParNew 類似, Parallel Scavenge 也是使用複製算法, 也是並行多線程收集器. 但與其他收集器關注儘可能縮短垃圾收集時間不同, Parallel Scavenge 更關注系統吞吐量:

系統吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)

停頓時間越短就越適用於用戶交互的程序-良好的響應速度能提升用戶的體驗;而高吞吐量則適用於後臺運算而不需要太多交互的任務-可以最高效率地利用CPU時間,儘快地完成程序的運算任務. Parallel Scavenge 提供瞭如下參數設置系統吞吐量:

 



 

 

3.3.4 Serial Old 收集器

Serial Old Serial 收集器的老年代版本, 同樣是單線程收集器,使用"標記-整理"算法

 



 

3.3.5 Parallel Old 收集器

Parallel Old Parallel Scavenge 收集器的老年代版本, 使用多線程和"標記-整理"算法, 吞吐量優先, 主要與 Parallel Scavenge 配合在注重吞吐量及 CPU 資源敏感系統內使用;

 



 

3.3.6 CMS 收集器(Concurrent Mark Sweep

CMS(Concurrent Mark Sweep)收集器是一款具有劃時代意義的收集器, 一款真正意義上的併發收集器, 雖然現在已經有了理論意義上表現更好的 G1 收集器, 但現在主流互聯網企業線上選用的仍是 CMS(Taobao、微店).

CMS是一種以獲取最短回收停頓時間爲目標的收集器(CMS又稱多併發低暫停的收集器), 基於"標記-清除"算法實現, 整個 GC 過程分爲以下 4 個步驟:

1. 初始標記(CMS initial mark)

2. 併發標記(CMS concurrent mark: GC Roots Tracing 過程)

3. 重新標記(CMS remark)

4. 併發清除(CMS concurrent sweep: 已死對象將會就地釋放, 注意:此處沒有壓縮)

其中 13 兩個步驟(初始標記、重新標記)仍需 STW. 但初始標記僅只標記一下 GC Roots 能直接關聯到的對象, 速度很快; 而重新標記則是爲了修正併發標記期間因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄, 雖然一般比初始標記階段稍長, 但要遠小於併發標記時間.

 



 

CMS 特點:

1. CMS 默認啓動的回收線程數=(CPU 數目+3)4

CPU >4 , GC 線程一般佔用不超過 25%CPU 資源, 但是當 CPU <=4 , GC 線程可能就會過多的佔用用戶 CPU 資源, 從而導致應用程序變慢, 總吞吐量降低.

2.無法處理浮動垃圾, 可能出現 Promotion FailureConcurrent Mode Failure 而導致另一次 Full GC 的產生: 浮動垃圾是指在 CMS 併發清理階段用戶線程運行而產生的新垃圾. 由於在 GC 階段用戶線程還需運行, 因此還需要預留足夠的內存空間給用戶線程使用, 導致 CMS 不能像其他收集器那樣等到老年代幾乎填滿了再進行收集. 因此 CMS 提供了

-XX:CMSInitiatingOccupancyFraction 參數來設置 GC 的觸發百分比 ( 以及 -XX:+UseCMSInitiatingOccupancyOnly 來啓用該觸發百分比), 當老年代的使用空間超過該比例後 CMS 就會被觸發(JDK 1.6 之後默認 92%). 但當 CMS 運行期間預留的內存無法滿足程序需要, 就會出現上述 Promotion Failure 等失敗, 這時 VM 將啓動後備預案: 臨時啓用 Serial Old 收集器來重新執行Full GC(CMS通常配合大內存使用, 一旦大內存轉入串行的Serial GC, 那停頓的時間就是大家都不願看到的了).

3.最後, 由於 CMS 採用"標記-清除"算法實現, 可能會產生大量內存碎片. 內存碎片過多可能會導致無法分配大對象而提前觸發 Full GC. 因此 CMS 提供了 -XX:+UseCMSCompactAtFullCollection 開關參數, 用於在 Full GC 後再執行一個碎片整理過程. 但內存整理是無法併發的, 內存碎片問題雖然沒有了, 但停頓時間也因此變長了, 因此 CMS 還提供了另外一個參數-XX:CMSFullGCsBeforeCompaction 用於設置在執行 N 次不進行內存整理的 Full GC , 跟着來一次帶整理的(默認爲 0: 每次進入 Full GC 時都進行碎片整理).

3.3.7 分區收集- G1 收集器

G1(Garbage-First)是一款面向服務端應用的收集器, 主要目標用於配備多顆 CPU 的服務器治理大內存.

- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector

(CMS).

-XX:+UseG1GC 啓用 G1 收集器.

與其他基於分代的收集器不同, G1 將整個 Java 堆劃分爲多個大小相等的獨立區域 (Region), 雖然還保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔離的了, 它們都是一部分 Region(不需要連續)的集合.如:

 



 

每塊區域既有可能屬於 O 區、也有可能是 Y , 因此不需要一次就對整個老年代/新生代回收. 而是當線程併發尋找可回收的對象時, 有些區塊包含可回收的對象要比其他區塊多很多. 雖然在清理這些區塊時 G1 仍然需要暫停應用線程, 但可以用相對較少的時間優先回收垃圾較多的 Region. 這種方式保證了 G1 可以在有限的時間內獲取儘可能高的收集效率.

G1的新生代收集跟ParNew類似: 存活的對象被轉移到一個/多個Survivor Regions. 如果存活時間達到閥值, 這部分對象就會被提升到老年代.如圖:

其特定是:一整塊堆內存被分爲多個 Regions. 存活對象被拷貝到新的 Survivor 區或老年代. 年輕代內存由一組不連續的 heap 區組成, 這種方法使得可以動態調整各代區域尺寸.

Young GC 會有 STW 事件, 進行時所有應用程序線程都會被暫停. 多線程併發 GC.

G1 老年代GC 特點如下: 併發標記階段

1 在與應用程序併發執行的過程中會計算活躍度信息.

2 這些活躍度信息標識出那些regions 最適合在STW 期間回收(which regions will be best to reclaim during an evacuation pause).

3 不像CMS 有清理階段.

再次標記階段

1 使用Snapshot-at-the-Beginning(SATB)算法比CMS 快得多.

2 空region 直接被回收.

拷貝/清理階段(Copying/Cleanup Phase)

1 年輕代與老年代同時回收.

2 老年代內存回收會基於他的活躍度信息.

4 JVM 優化

4.1 JDK 常用 JVM 優化相關命令

 


 

 

 



 

 

4.1.1 jps

jps - l

顯示線程 id 和執行線程的主類名

jps -v

顯示線程 id 和執行線程的主類名和 JVM 配置信息

 



 

4.1.2 jstat

jstat -參數 線程 id 執行時間(單位毫秒) 執行次數

jstat -gc 4488 30 10

 



 

SXC - survivor 初始空間大小,單位字節。

SXU - survivor 使用空間大小, 單位字節。

EC - eden 初始空間大小

EU - eden 使用空間大小

OC - old 初始空間大小

OU - old 使用空間大小

PC - permanent 初始空間大小

PU - permanent 使用空間大小

YGC - youngGC 收集次數

YGCT - youngGC 收集使用時長, 單位秒

FGC - fullGC 收集次數

FGCT - fullGC 收集使用時長

GCT - 總計收集使用總時長 YGCT+FGCT

 

4.1.3 jvisualvm

一個 JDK 內置的圖形化 VM 監視管理工具

 



 

4.1.4 visualgc 插件

 



 

重啓 jvisualvm 工具

 


 

4.2 JVM 常見參數

配置方式:java [options] MainClass [arguments]

options - JVM 啓動參數。 配置多個參數的時候,參數之間使用空格分隔。

參數命名: 常見爲 -參數名

參數賦值: 常見爲 -參數名=參數值 | -參數名:參數值

4.2.1 內存設置

-Xms:初始堆大小,JVM 啓動的時候,給定堆空間大小。

-Xmx:最大堆大小,JVM 運行過程中,如果初始堆空間不足的時候,最大可以擴展到多少。

-Xmn:設置年輕代大小。整個堆大小=年輕代大小+年老代大小+持久代大小。持久代一般固定大小爲 64m,所以增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,

Sun 官方推薦配置爲整個堆的 3/8

-Xss: 設置每個線程的 Java 棧大小。JDK5.0 以後每個線程 Java 棧大小爲 1M,以前每個線程堆棧大小爲 256K。根據應用的線程所需內存大小進行調整。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在 3000~5000 左右。

-XX:NewSize=n:設置年輕代大小

-XX:NewRatio=n:設置年輕代和年老代的比值。如:3,表示年輕代與年老代比值爲 1

3,年輕代佔整個年輕代+年老代和的 1/4

-XX:SurvivorRatio=n:年輕代中 Eden 區與兩個 Survivor 區的比值。注意 Survivor 區有兩個。

如:3,表示 EdenSurvivor=32,一個 Survivor 區佔整個年輕代的 1/5

-XX:MaxPermSize=n:設置持久代大小

-XX:MaxTenuringThreshold:設置垃圾最大年齡。如果設置爲 0 的話,則年輕代對象不經過 Survivor 區,直接進入年老代。對於年老代比較多的應用,可以提高效率。如果將此值設置爲一個較大值,則年輕代對象會在 Survivor 區進行多次複製,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概率。

4.2.2 內存設置經驗分享

JVM 中最大堆大小有三方面限制:相關操作系統的數據模型(32-bt 還是 64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。32 位系統 下,一般限制在 1.5G~2G64 爲操作系統對內存無限制。

Tomcat 配置方式: 編寫catalina.bat|catalina.sh,增加JAVA_OPTS 參數設置。windows linux 配置方式不同。windows - set "JAVA_OPTS=%JAVA_OPTS% 自定義參數"linux -

JAVA_OPTS="$JAVA_OPTS 自定義參數" 常見設置:

-Xmx3550m -Xms3550m -Xmn2g -Xss128k 適合開發過程的測試應用。要求物理內存大於 4G

-Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4

-XX:MaxPermSize=160m -XX:MaxTenuringThreshold=0 適合高併發本地測試使用。且大數據對

象相對較多(如 IO 流)

環境: 16G 物理內存,高併發服務,重量級對象中等(線程池,連接池等),常用對象

比例爲 40%(運行過程中產生的對象 40%是生命週期較長的)

-Xmx10G -Xms10G -Xss1M -XX:NewRatio=3 -XX:SurvivorRatio=4 -XX:MaxPermSize=2048m

-XX:MaxTenuringThreshold=5

4.2.3 收集器設置

收集器配置的時候,次收集器和全收集器必須匹配。具體匹配規則參考 3.1.3

-XX:+UseSerialGC:設置串行收集器,年輕帶收集器, 次收集器

-XX:+UseParallelGC:設置並行收集器

-XX:+UseParNewGC:設置年輕代爲並行收集。可與 CMS 收集同時使用。JDK5.0 以上,JVM 會根據系統配置自行設置,所以無需再設置此值。

-XX:+UseParallelOldGC:設置並行年老代收集器,JDK6.0 支持對年老代並行收集。

-XX:+UseConcMarkSweepGC:設置年老代併發收集器,測試中配置這個以後,-XX:NewRatio 的配置失效,原因不明。所以,此時年輕代大小最好用-Xmn 設置。

-XX:+UseG1GC:設置 G1 收集器

4.2.4 垃圾回收統計信息

類似日誌的配置信息。會有控制檯相關信息輸出。 商業項目上線的時候,不允許使用。一定使用 loggc

-XX:+PrintGC

-XX:+Printetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

4.2.5 並行收集器設置

-XX:ParallelGCThreads=n:設置並行收集器收集時最大線程數使用的 CPU 數。並行收集線程數。

-XX:MaxGCPauseMillis=n:設置並行收集最大暫停時間,單位毫秒。可以減少 STW 時間。

-XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比。公式爲 1/(1+n)併發收集器設置

-XX:+CMSIncrementalMode:設置爲增量模式。適用於單 CPU 情況。

-XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動選擇年輕代區大小和相應的 Survivor 區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直打開。

-XX:CMSFullGCsBeforeCompaction=n:由於併發收集器不對內存空間進行壓縮、整理,所以運行一段時間以後會產生"碎片",使得運行效率降低。此值設置運行多少次 GC 以後對內存空間進行壓縮、整理。

-XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,但是可以消除碎片

4.2.6 收集器設置經驗分享

關於收集器的選擇 JVM 給了三種選擇:串行收集器、並行收集器、併發收集器,但是

串行收集器只適用於小數據量的情況,所以這裏的選擇主要針對並行收集器和併發收集器。默認情況下,JDK5.0 以前都是使用串行收集器,如果想使用其他收集器需要在啓動時加入相應參數。JDK5.0 以後,JVM 會根據當前系統配置進行判斷。

常見配置:並行收集器主要以到達一定的吞吐量爲目標,適用於科學計算和後臺處理等。

-Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

使用 ParallelGC 作爲並行收集器, GC 線程爲 20CPU 核心數>=20 時),內存問題根據

硬件配置具體提供。建議使用物理內存的 80%左右作爲 JVM 內存容量。

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelOldGC

指定老年代收集器,在JDK5.0之後的版本,ParallelGC對應的全收集器就是ParallelOldGC

可以忽略

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

指定 GC 時最大暫停時間。單位是毫秒。每次 GC 最長使用 100 毫秒。可以儘可能提高

工作線程的執行資源。

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

-XX:+UseAdaptiveSizePolicy

UseAdaptiveSizePolicy 是提高年輕代 GC 效率的配置。次收集器執行效率。

併發收集器主要是保證系統的響應時間,減少垃圾收集時的停頓時間。適用於應用服務器、電信領域、互聯網領域等。

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20

-XX:+UseConcMarkSweepGC -XX:+UseParNewGC

指定年輕代收集器爲 ParNew,年老代收集器 ConcurrentMarkSweep,併發 GC 線程數爲 20CPU 核心>=20),併發 GC 的線程數建議使用(CPU 核心數+3/4 CPU 核心數【不推薦使用】。

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC

-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

CMSFullGCsBeforeCompaction=5 執行 5 GC 後,運行一次內存的整理。

UseCMSCompactAtFullCollection 執行老年代內存整理。可以避免內存碎片,提高 GC 過程中的效率,減少停頓時間。

4.2.7 簡單總結

年輕代大小選擇

響應時間優先的應用:儘可能設大,直到接近系統的最低響應時間限制(根據實際情況選擇)。在此種情況下,年輕代收集發生的頻率也是最小的。同時,減少到達年老代的對象。

吞吐量優先的應用:儘可能的設置大,可能到達 Gbit 的程度。因爲對響應時間沒有要求,垃圾收集可以並行進行,一般適合 8CPU 以上的應用。

年老代大小選擇

響應時間優先的應用:年老代使用併發收集器,所以其大小需要小心設置,一般要考慮併發會話率和會話持續時間等一些參數。如果堆設置小了,可以會造成內存碎片、高回收頻率以及應用暫停而使用傳統的標記清除方式;如果堆大了,則需要較長的收集時間。

最優化的方案,一般需要參考以下數據獲得:併發垃圾收集信息持久代併發收集次數

傳統GC信息花在年輕代和年老代回收上的時間比例減少年輕代和年老代花費的時間,一般會提高應用的效率

吞吐量優先的應用:一般吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代。原因是,這樣可以儘可能回收掉大部分短期對象,減少中期的對象,而年老代存放長期存活對象。

較小堆引起的碎片問題,因爲年老代的併發收集器使用標記、清除算法,所以不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合併,這樣可以分配給較大的對象。但是,當堆空間較小時,運行一段時間以後,就會出現"碎片",如果併發收集器找不到足夠的空間,那麼併發收集器將會停止,然後使用傳統的標記、整理方式進行回收。如果出現"碎片",可能需要進行如下配置: -XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啓對年老代的壓縮。

-XX:CMSFullGCsBeforeCompaction=0:上面配置開啓的情況下,這裏設置多少次 Full GC

後,對年老代進行壓縮

4.2.8 測試代碼

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章