標籤:
一,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情況
詳細 附上
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,記憶體回收和調優原理打完收工,大概清楚 ,調優了就有方向了,具體參數可以查文檔等
【轉】jvm 記憶體模型及記憶體調優