hotspot的垃圾回收策略,設置和調優

引言:java

咱們都知道JVM內存由幾個部分組成 Java棧、程序計數器(ProgramCounter)寄存器、本地方法棧、堆、方法區、運行常量池。linux

JVM垃圾回收僅僅針對公共內存區域即:方法區進行。算法

本文主要討論兩點,一是垃圾回收策略,二是調優的方法。windows

1、垃圾回收機制

1.1 分代管理

方法區按照對象出現的不一樣時間進行分代:tomcat

u  堆中會頻繁地建立對象,基於一種分代的思想,按照對象存活時間將劃分爲新生代舊生代兩部分,咱們不能一次垃圾回收新生代存活的對象就放入舊生代,而是要通過幾回GC後還存活的對象,咱們才放入舊生代,因此咱們又把新生代再次劃分爲Eden區和兩個Survivor區,讓對象建立在Eden區,而後在兩個Survivor之間反覆複製,最後仍然存活的對象才複製到舊生代中。網絡

方法區存放的是常量、加載的字節碼文件信息等,信息相對穩定。由於不會頻繁地建立對象,因此不須要分代,直接GC便可。多線程

由此咱們JVM垃圾回收要掃描的範圍是:併發

注:圖片來自網絡app

新生代:工具

1.全部新對象建立發生在Eden區,Eden區滿後觸發新生代上的minor GC,將Eden區和非空閒Survivor區存活對象複製到另外一個空閒的Survivor區中。

2.永遠保證一個Survivor是空的,新生代minor GC就是在兩個Survivor區之間相互複製存活對象,直到Survivor區滿爲止。

舊生代:

1.Eden區滿後觸發minor GC將存活對象複製到Survivor區,Survivor區滿後觸發minor GC將存活對象複製到舊生代。

2.通過新生代的兩個Survivor之間屢次複製,仍然存活下來的對象就是年齡相對比較老的,就能夠放入到舊生代了,隨着時間推移,若是舊生代也滿了,將觸發Full GC,針對整個堆(有新生代、舊生代和持久代)進行垃圾回收。

持久代:

持久代若是滿,將觸發Full GC

1.2 垃圾回收

要執行gc關鍵在於兩點,一是檢測垃圾對象,二是釋放垃圾對象所佔用的空間。

1.2.1 檢測垃圾對象

檢測出垃圾對象通常有兩種算法

一、 引用計數法

二、 可達性分析

引用計數法由於沒法檢測對象之間相互循環引用的問題,基本沒有被採用。如今主流的語言的垃圾收集中檢測垃圾對象主要仍是「可達性分析」方法,下面也主要介紹JVM可達性分析方法檢測垃圾對象。

「可達性分析」算法描述?

經過一系列的名爲「GC Root」的對象做爲起點,從這些節點向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則該對象不可達,該對象是不可以使用的,垃圾收集器將回收其所佔的內存。因此JVM判斷對象須要存活的原則是:可以被一個根對象到達的對象。

什麼是可以到達呢?

就是對象A中引用了對象B,那麼就稱A到B可達

GCRoot對象集合?

a.Java虛擬機棧(棧幀中的本地變量表)中的引用的對象。

b.方法區中的類靜態屬性引用的對象。

c.方法區中的常量引用的對象。

d.本地方法棧中JNI本地方法的引用對象。

1.2.2 釋放空間

一、垃圾回收算法

前面已經介紹瞭如何檢測出垃圾對象,在檢測出垃圾對象以後,須要按照特定的垃圾回收算法進行內存回收,常見的垃圾回收算法有:

u  複製(Copying)

u  標記-清除(Mark-Sweep)

u  標記-整理(Mark-Compact)

u  分代(Generational Collection),藉助前面三種算法實現

這裏就不一一詳述,感興趣能夠自行百度。

二、垃圾收集器實現

上面算法都是理論性的東西,Java虛擬機規範沒有規定垃圾收集器具體如何實現,所以不一樣廠商、不一樣版本虛擬機提供的垃圾收集器可能有所差別。下面列舉HotSpot(Sun JDK和Open JDK自帶)虛擬機提供的六種垃圾收集器實現:

收集器名稱

應用目標

採用算法

引入版本

運行方式

Serial

新生代

複製算法

Jdk1.3.1前

串行,單線程

ParNew

新生代

複製算法

 

並行,多線程

Parallel Scavenge

新生代

複製算法

Jdk1.4

並行,多線程

Serial Old

舊生代

標記-整理

 

串行,單線程

Parallel Old

舊生代

標記-整理

Jdk1.6

並行,多線程

CMS

舊生代

標記-清除

Jdk1.5

併發,多線程

並行(Parallel):多條垃圾收集線程並行工做,而用戶線程仍處於等待狀態。

併發(Concurrent):垃圾收集線程與用戶線程一段時間內同時工做(不是並行,而是交替執行)。

總結:

一、兩個串行收集器、三個並行收集器、一個併發收集器。

二、ParNew收集器是Serial的多線程版本。

三、Serial Old收集器是Serial收集器的舊生代版本。

四、Parallel Scavenge收集器以吞吐量爲目標,適合在後臺運算而不須要太多交互的任務。

五、Parallel Old收集器是Parallel Scavenge的舊生代版本。

六、Parallel Scavenge收集器和Parallel Old收集器是名副其實的「吞吐量優先」組合。

七、除CMS外,其餘收集器工做時都須要暫停其餘全部線程,CMS是第一款真正意義上的併發(Concurrent)收集器,第一次實現了讓垃圾收 集器線程與 用戶線程同時工做,是一款以最短停頓時間爲目標的收集器,適合交互性較多的場景,這也是與Parallel Scavenge/Parallel Old吞吐量優先組合的區別。

八、新生代由於回收留下的對象少,因此採用標記-複製法。

九、舊生代由於回收留下的對象多,因此採用標記-清除/標記-整理算法。

三、選擇所需垃圾收集器

虛擬機提供了參數,以便用戶根據本身的需求設置所需的垃圾收集器:

JVM運行參數

新生代

舊生代

-XX:+UseSerialGC(Client模式默認值)

Serial

Serial Old

-XX:+UseParNewGC

ParNew

Serial Old

-XX:+UseConcMarkSweepGC

ParNew

CMS(Serial Old備用)

-XX:+UseParallelGC(Server模式默認值)

Parallel Scavenge

Serial Old

-XX:+UseParallelOldGC

Parallel Scavenge

Parallel Old

2、性能調優

2.1 性能調優的目的

減小minor gc的頻率、以及full gc的次數。

2.2 性能調優的手段

1.使用JDK提供的內存查看工具,如JConsoleJava VisualVM

2.控制堆內存各個部分所佔的比例

3.採用合適的垃圾收集器

手段1:內存查看工具和GC日誌分析

n  -verbose.gc:顯示GC的操做內容。打開它,能夠顯示最忙和最空閒收集行爲發生的時間、收集先後的內存大小、收集須要的時間等。

n  -xx:+printGCdetails:詳細瞭解GC中的變化。

n  -XX:+PrintGCTimeStamps:瞭解這些垃圾收集發生的時間,自JVM啓動之後以秒計量。

n  -xx:+PrintHeapAtGC:瞭解堆的更詳細的信息。

手段2:針對新生代和舊生代的比例

若是新生代過小,會致使頻繁GC,並且大對象對直接進入舊生代引起full gc

若是新生代太大,會誘發舊生代full gc,並且新生代的gc耗時會延長

建議新生代佔整個堆1/3合適,相關JVM參數以下:

n  -Xms:初始堆大小

n  -Xmx:最大堆大小

n  - Xmn:新生代大小

n  -XX:PermSize=n:持久代最大值

n  -XX:MaxPermSize=n:持久代最大值

n  -XX:NewRatio=n:設置新生代和舊生代的比值。如:爲3,表示新生代與舊生代比值爲1:3,新生代佔整個新生代舊生代和的1/4。

手段3:針對Eden和Survivor的比例

若是Eden過小,會致使頻繁GC

若是Eden太大,會致使大對象直接進入舊生代,下降對象在新生代存活時間。

n  -XX:SurvivorRatio=n:新生代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5

n  -XX:PretenureSizeThreshold:直接進入舊生代中的對象大小,設置此值後,大於這個參數的對象將直接在舊生代中進行內存分配。

n  -XX:MaxTenuringThreshold:對象轉移到舊生代中的年齡,每一個對象經歷過一次新生代GC(Minor GC)後,年齡就加1,到超過設置的值後,對象轉移到舊生代。

手段4:採用正確的垃圾收集器

經過JVM參數設置所使用的垃圾收集器參考前面的介紹,這裏關注其餘一些設置。

並行收集器設置

n  -XX:ParallelGCThreads=n:設置並行收集器收集時並行收集線程數。

n  -XX:MaxGCPauseMillis=n:設置並行收集最大暫停時間,僅對ParallelScavenge生效。

n  -XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比,僅對Parallel Scavenge生效。

併發收集器設置

n  -XX:CMSInitiatingOccupancyFraction:默認設置下,CMS收集器在舊生代使用了68%的空間後就會被激活。此參數就是 設置舊生代空間被使用多少後觸發垃圾收集。注意要是CMS運行期間預留的內存沒法知足程序須要,就會出現concurrent mode failure,這時候就會啓用Serial Old收集器做爲備用進行舊生代的垃圾收集。

n  -XX:+UseCMSCompactAtFullCollection:空間碎片過可能是標記-清除算法的弊端,此參數設置在FULL GC後再進行一個碎片整理過程

n  -XX:CMSFullGCsBeforeCompaction:設置在若干次垃圾收集以後再啓動一次內存碎片整理

3、實際項目配置

 

一、修改 tomcat\bin\Catalina.bat 文件
windows環境下:

在166行左右
rem Execute Java with the applicable properties 」如下每行
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%

在 %DEBUG_OPTS% 後面添加-Xms256m -Xmx512m

linux環境下:

打開在Tomcat的安裝目錄的bin文件的catalina.sh文件,進入編輯狀態.
在註釋後面加上以下腳本:
JAVA_OPTS='-Xms512m -Xmx1024m'
JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=64M -XX:MaxPermSize=256m"

其中 JAVA_OPTS='-Xms512m -Xmx1024m' 是設置Tomcat使用的內存的大小.

-XX:PermSize=64M -XX:MaxPermSize=256m 指定類空間(用於加載類)的內存大小 

保存後,從新以命令行的方式運行 tomcat ,便可,而後經過最後面介紹的如何觀察tomcat現有內存狀況的方法進行查看是否已經變動成功。

相關文章
相關標籤/搜索