二,JVM的內存模型,(GC)垃圾處理,調優監控(基於HotSpot VM,JDK1.5+)【JAVA內存模型】


一,JVM內存模型概括 

 

image

還有一個寄存器,線程運行於其上面

 

1.程序計數器
記錄線程的執行位置,線程私有內存,唯一一個在Java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域

2.線程棧(VM stack)

image

棧的默認大小是1M

-Xss2m 這樣設置成2M

異常 :Fatal: Stack size too small

異常的引起一般是線程數目太多


3.本地方法棧(native stack)


即為一些Native方法分配的stack

異常:java.lang.OutOfMemoryError: unable to create new native thread

一般也是由線程太多引起,增加棧空間,同上方法

 

4.堆(heap),程序可用堆

image

截圖自JConsole
每個線程的棧都是該線程私有的,堆則是所有線程共享的

這里說的堆,主要指程序能控制的,包括

image

The New Generational Heap,默認4M,此區域一般為JVM內存的1/15大小

此代分為Eden space區,Survivo space區可以看成emptySurvivo區,Survivor區,

當new 一個對象時,首先是在Eden space區,當Eden space區滿時,Survivor space區進行垃圾回收(此處復制算法),當對象在Survivo space區經過幾次回收Tenured Generation

復制算法,每次算法開始都得停止當前所有的線程,然后把Survivor區的所有活躍的對象復制到emptySurvivo區,然后對Survivor區空間進行清除變成emptySurvivo,以前的emptySurvivo成為了Survivor區。(互換)

 

Tenured Generation

此處儲存年老代的對象,此處的垃圾回收采用The Mark and Sweep 算法

GC標記算法/清理算法(The Mark and Sweep algorithms)進行回收,從引用進行標記,然后按照引用的程度或無引用到的對象進行回收,然后再對清除了的內存進行合並.

 

關於GC,因為GC主要就是對堆的回收,當然還有常量池和永久代,所以此處總結下GC

GC策略介紹

對於GC在 HotSpot VM 常用的有三種:

1.serial collector,單線程收集器,回收時都需要暫停當前線程,長時間等待,

配置

Client下默認方式

強制加上 -XX:+UseSerialGC

2.parallel collector( throughput collector ),並行收集器,或叫多線程的收集

年輕代:暫停應用程序,多個垃圾收集線程並行的復制收集。
年老代:暫停應用程序, 多個垃圾收集線程並行的復制收集。

server下默認方式,具體配置

設置並行收集的線程數目,如20個線程,-XX:ParallelGCThreads=20

配置年輕代為並行收集  -XX:+UseParallelGC.

配置年老代垃圾收集方式為並行收集(JDK6.0開始支持)-XX:+UseParallelOldGC

設置暫停時間,設置每次年輕代垃圾回收的最長時間如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值,如 -XX:MaxGCPauseMillis= 100, 

設置吞吐量,吞吐量為垃圾回收時間與非垃圾回收時間的比值  -XX:GCTimeRatio 來調整GC的時間

 

3.concurrent collector(concurrent low pause collector),並發收集器

年輕代:同樣是暫停應用程序,多個垃圾收集線程並行的復制收集。
年老代:和並行的區別在這,只是在初始標記(initial mark)和二次標記(remark)時需要stop-the-world。但收集時時間很長,所以不能等年輕代滿后再開始清理.

使用啟動並發收集器 -XX:+UseConcMarkSweepGC

XX:CMSInitiatingOccupancyFraction=指定還有多少剩余堆時開始執行並發收集

 

  • 根據官方文檔,他們倆個需要在多CPU的情況下,才能發揮作用。在一個CPU的情況下,會不如默認的serial collector,因為線程管理需要耗費CPU資源。而在兩個CPU的情況下,也提高不大。只是在更多CPU的情況下,才會有所提高。當然 concurrent low pause collector有一種模式可以在CPU較少的機器上,提供盡可能少的停頓的模式,見CMS GC Incremental mode。
  • 當要使用throughput collector時,在java opt里加上-XX:+UseParallelGC,啟動throughput collector收集。也可加上-XX:ParallelGCThreads=<desired number>來改變線程數。還有兩個參數 -XX:MaxGCPauseMillis=<nnn>和 -XX:GCTimeRatio=<nnn>,MaxGCPauseMillis=<nnn>用來控制最大暫停時間,而-XX: GCTimeRatio可以提高GC說占CPU的比,以最大話的減小heap。

- 此段引自 HotSpot VM GC 的種類 

 

GC的觸發條件(基於serial collector,不同的GC策略略有不同,基本都差不多)

對於new generation來說,當eden區的對象空大於Survivor0(假設為from)的free空間時,會發生minor gc,即對整個Survivor0區,Survivor1區進行一個復制算法垃圾回收,並且部分對象轉到Old Generation

當new generation滿了(即eden區+Survivor0區大於Old Generation的free空間時),將發生major gc,即對New Generation和Old Generation兩代都進行垃圾回收

當然還一種程序調用system.gc(),但此方法也不一定會調用,只是建議

所以得出,如果JVM的設置內存過大,發生GC的回收頻率將越小,但是回收的時間越長(特別對new generation進行復制算法的復制時是需要停止當前所有線程的),所以並不是說JVM的內存設置的越大越好,得根據實際情況進行優化。

 

異常 java.lang.OutOfMemoryError: Java heap space

一般是由於垃圾回收后,old generation里空間也不夠用了

 

 

堆內存的相關參數設置

默認值(基於-server)

-server時最大堆內存是物理內存的1/4,但小於1G. JDK 1.5以前是64M

(官方: Smaller of 1/4th of the physical memory or 1GB.Before J2SE 5.0, the default maximum heap size was 64MB.)

-client 小一倍

參數設置

-Xms128m
表示JVM Heap(堆內存)最小尺寸128MB,初始分配
-Xmx512m
表示JVM Heap(堆內存)最大允許的尺寸256MB,按需分配

 

new Generation與Old Generation的比例,默認為1:2,即為2

-XX:NewRatio= 參數可以設置(也可以-XX:NewSize和-XX:MaxNewsize設置新域的初始值和最大值)

 

Eden與Survivor的比例,默認為32

-XX:SurvivorRation=參數可以設置

 

當對象默認經過1次New Generation 就轉入Old Generation(這個不同文章上不同,待我確定

-XX:MaxTenuringThreshold=參數可以設置 (默認0)

用-XX:+PrintTenuringDistributio可以查看值

 


5.方法區,永久代(Perm Space)

其實也可與看成堆內存的一部分,看成永久代(Perm Space),但GC也會回收,但很少回收,它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據,比如一個int x,在不同的環境中是不同的,此信息就存在方法區

 

異常:java.lang.OutOfMemoryError: PermGen space

引起,一般是太多的類信息,比如應用spring很多反射信息,可以適度設置大點

方法區或永生代相關設置

-XX:PermSize=64MB 最小尺寸,初始分配
-XX:MaxPermSize=256MB 最大允許分配尺寸,按需分配

XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 設置垃圾不回收

默認大小

-server選項下默認MaxPermSize為64m
-client選項下默認MaxPermSize為32m

 


6.常量池
Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量表(constant_pool table),用於存放編譯期已可知的常量,這部分內容將在類加載后進入方法區(永久代)存放。但是Java語言並不要求常量一定只有編譯期預置入Class的常量表的內容才能進入方法區常量池,運行期間也可將新內容放入常量池(最典型的String.intern()方法)。

GC的作用主要是用來卸載類和回收常量池,當然有部分方法區,即使永久代(Perm Space)也會一定的回收

 

7.native內存區“Code Cache”(non-heap)

這個沒畫出來,但通過工具可以看到,用於存放編譯和保存本地代碼(native code)的內存

 

 

 

二,對TOMCAT監控調優

1,監控TOMCAT內存

JConsole,JDK自帶

直接在命令行輸入JConsole,打開JConsole,如堆那個圖,就可以監控了

遠程監控,需要在啟動程序時,配置 com.sun.management.jmxremote 即可

比如tomcat,

打開%tomcat_home%/bin/catalina.bat (linux下是catalina.sh),在JAVA_OPTS=%JAVA_OPTS%后加上參數

-Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

然后打開JConsole

image

然后連接即可

image

如打開堆圖,可以找到上面說的各個部分

imageimage

 

Eden Space (heap): 內存最初從這個線程池分配給大部分對象。

Survivor Space(heap):用於保存從Eden Space copy的對象,復制算法,分兩部分Survivor

Tenured Generation (heap):用於保持已經在 survivor space內存池中存在了一段時間的對象。

Perm Generation (non-heap): 方法區,保存虛擬機自己的靜態(refective)數據,例如類(class)和方法(method)對象。Java虛擬機共享這些類數據。這個區域被分割為只讀的和只寫的

Code Cache(non-heap):HotSpot Java虛擬機包括一個用於編譯和保存本地代碼(native code)的內存,叫做“代碼緩存區”(code cache)

 

2,打印Tomcat的日志

在前面繼續加上參數 -Xloggc:%TOMCAT_HOME%\logs\tomcat_gc.log  , 把LOG日志文件輸出到tomcat_gc.log 

打開日志如下

0.755: [GC 11747K->1664K(62848K), 0.0039655 secs]
0.759: [Full GC 1664K->1604K(62848K), 0.0241215 secs]

如果不滿意,可以進行調優,參數如文中上面參數所示,直接設置於JAVA_OTPS即可

3,利用JSTA查看GC情況

 

詳細 附上

JConsole 遠程監控Tomcat服務

TOMCAT參數詳解

jstat 使用

其他工具

 

JProfiler :商業軟件,需要付費。功能強大。詳細說明參考

VisualVM :JDK自帶,功能強大,與JProfiler類似,官網地址下載http://visualvm.java.net/download.html(當然JDK7現在都自帶了,在jdk1.7.0_10\bin里)

JConsole和VisualVM 在JDK都自帶了 o(∩_∩)o 哈哈  感覺JProfiler這些收費的挺尷尬

 

OK,垃圾回收和調優原理打完收工,大概清楚 ,調優了就有方向了,具體參數可以查文檔等

 

本文是基於HotSpot VM,其它JVM可能有些不一樣,累死了,歡迎交流QQ107966750


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM