一,JVM內存模型概括
還有一個寄存器,線程運行於其上面
1.程序計數器
記錄線程的執行位置,線程私有內存,唯一一個在Java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域
2.線程棧(VM stack)
棧的默認大小是1M
-Xss2m 這樣設置成2M
異常 :Fatal: Stack size too small
異常的引起一般是線程數目太多
3.本地方法棧(native stack)
即為一些Native方法分配的stack
異常:java.lang.OutOfMemoryError: unable to create new native thread
一般也是由線程太多引起,增加棧空間,同上方法
4.堆(heap),程序可用堆
截圖自JConsole
每個線程的棧都是該線程私有的,堆則是所有線程共享的
這里說的堆,主要指程序能控制的,包括
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
然后連接即可
如打開堆圖,可以找到上面說的各個部分
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情況
詳細 附上
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