jvm 的內存結構
jvm 是按照運行時數據的存儲結構來劃分內存結構的,jvm在運行java 程序時,將他們劃分成幾 種不同格式的數據,分別存儲在不同的區域,這些數據統一稱爲運行時數據。運行時數據包括java 程序本身的數據信息和jvm運行java 需要額外的數據信息。
jvm 運行時數據區
程序技術器-----線程私有
java 虛擬機棧--線程私有
本地方法棧-----線程私有
java堆----------線程公用
方法區----------線程公用
jvm 內存分配
保存參數、局部變量、中間計算過程和其他數據,退出方法的時候,修改棧頂指針就就可以把棧幀中的內容銷燬。
棧的優點:存取數據比堆快,僅此與寄存器,棧數據可以共享
棧的缺點:存在棧中的數據大小,生存期是在編輯時就確定的,導致其缺乏靈活性。
-Xss 每個線程使用的內存
堆的有點:動態地分配內存大小,生存期不必事先告訴編輯器,它是在運行期動態動配分配的垃圾回收器會自動收走不在使用的空間區域
堆內存機構如下圖:
年輕代:有eden 區和from 、to 組成,可以是說是e 區s1 s2 區之間的工作模式是開始線程全部進入eden 區域會後會通過copy 工作模式把需要的線程copy 到s1 或者s2 區域所有通過new 創建的對象內存都在堆中分配。其大小可以通過-Xms -Xmx 來控制,堆被劃分爲新生代和舊生代,新生代又被進一步劃分爲Eden 和Suruvivor 最後和Survior由formspace 和tospace 組成。
老年代大小=堆內存-新生代-持久帶 因爲老年代沒有參數可控制所以新生態越大老年代越小。
java 堆結構和垃圾回收
說明:Non-Heap 是非堆內存信息
Heap 堆內存信息也是新生代信息
jvm 堆配置參數
1、-Xms 初始化堆內存大小 默認是物理內存的1/64 (<1GB)
2、-Xmx 最大的堆大小 默認是物理內存的1/4(1<1GB),實際中建議大於4Gb
3、一般建議設置-Xms=-Xmx 好處是避免每次gc 後調整堆內存大小,減少系統內存分配的開銷
4、整個堆大小=年輕代大小+老年代大小+持久代大小
jvm 新生代(young generation)
1、新生代=1和ede區 和兩個subrvivor區
2、-Xmn 年輕代大小(1.4版本之前通過 -xx:NewSize ,-XX:MaxNewSize)
3、-XX:NewRatio
年輕代與老年代的比值(除去持久代)
Xms=Xmx 並且設置了Xmn 的情況下,該參數不需要進行設置
4、-XX:SurvivorRatio
Eden區與Survivor區的大小比值,設置爲8,則兩個Survivor 區與一個eden 區比值爲2:8 Survivor區佔整個年輕代的1/10
5、用來存放java 對象剛分配的java 對象
jvm 老年代(teunred geneation)
1、老年代=整個堆-年輕代大小-持久代大小
2、年輕代中經過垃圾回收沒有回收掉的對象被複制到老年代
3、老年代存儲對象比年輕代年齡大的多,而且不缺乏大對象
4、新建的對象也有可能直接進入老年代
4.1 大對象,可以通過啓動參數設置-XX:PretenureSizeThreshold=1024(單位字節,默認爲0)來代表超過多大時就不再新生代分配。而是直接在老年代分配
4.2 大的數組對象,切數組中無引用外部對象
java 強引用 | java 弱引用 |
不會丟失數據,所以訪問速度快 線程開啓,停止服務也沒有辦法回收線程除非重啓系統 同時如果線程數到達java 設定的而無法進行處理那麼 服務就會冗機 | 丟失數據,會去數據庫讀取數據會慢一些,如果eden區和s1 、s2 到old 區50M 如果old區緩存爲40M在理想 的情況下正常數據從eden s1 s2 區到old 過來的數據不會超過1M 假如:老年代緩存爲50M 這是空閒空間爲5M 這時如果從新生代過來的數據超過5M 那麼系統會直接回收所有的空間,用來存放新的數據,緩存的數據會讀取數 庫,如果是強引用系統就會掛掉。 |
5、老年代大小無配置參數
java 持久代(perm generation)
1、持久代=整個堆-年輕代大小-老年代大小
2、-XX:PermSize -XX:MaxPermSize
設置持久代的大小,一般情況推薦把-XX:PermSize設置成-XX:MaxPermSize的值爲相同的值,因爲持久代大小的調整也會導致堆內存需要發出fgc
3、存放Class、Method元信息,其大小與項目的規模;類、方法的數量有關,一般設置爲128M 就足夠了,設置原則是預留30% 的空間。
4、永久代的回收方式
4.1、常量池中的常量,無用的類信息、常量的回收很簡單、沒有引用了就可以被回收
4.2、對於無用的類進行回收。必須保證3點:
類的所有實列都已經被回收
加載類的class loader 已經回收
類對象的class對象沒有被引用(即沒有通過發射引用該類的地方)
jvm 垃圾收集算法
1、引用計數算法
每個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0 可以收回,次方法簡單無解決對象相互循環引用的問題,還有一個問題是如果解決精準計數
2、根據搜索算法
從GC Rots 開始向下搜索,搜索所走過的路徑成爲引用鏈,當一個對象到GCRoots 沒有任何引用鏈相連時,則證明此對象是不可用,不可到達對象
在java 語言中,GCRoots 包括
虛擬機中引用的對象
方法區類靜態屬性實體引用的對象
方法區類靜態屬性實體引用的對象
方法區常量引用的對象
本地方法棧中JNI 引用的對象
jvm 垃圾回收算法
複製算法(copying)
標記清除算法(Mark-weep)
標記整理壓縮算法(Mark-Compac)
以下是重點*****
名詞解釋
1、穿行回收
gc 單線程內存回收,會暫停所有用戶線程
2、並行回收
手機是指多個gc 線程並行工作,但此用戶線程是暫停,所有sebul 是串行的,parallel (並行)收集器是並行的而cms 收集器是併發的
3、併發回收
是指用戶線程同時執行(不一定是並行可能是交替,但總體上在同時執行的)不需要停頓用戶線程,(其實在cms 中用戶線程還是需要停頓的,只是非常短,GC線程在另一個CPU 上執行)
穿行 並行 併發 ****G1 不使用
新生代Serial 回收器
1、-XX:+UseSerialGC來開啓 Serial New + Serial Old 的收集器組合進行內存回收
2、使用複製算法。
3、獨佔式的垃圾回收
缺點:一個線程進行GC 穿行。其他工作線程暫停
老年代Serial 回收器
1、-XX:+UseSerialGC 來開啓Serial New+Serial Old 的收集器組合進行內存回收
2、使用標記--壓縮算法
3、串行的、獨佔式的垃圾回收器
因爲內存比較大原因,回收比新生代慢
新生代ParNew 回收器
1、-XX:+UseParNewGC 開啓新生代使用並行回收收集器,老年代使用串行收集器
2、-XX:ParallelGCThreads 指定線程數默認最好與CPU數量相當,避免過多的線程數影響垃圾收集性能
3、使用複製算法
4、並行的、獨佔式的垃圾回收器
新生代Parallel Scavenge 回收器
1、吞吐量優先回收器
關注CPU 吞吐量,即運行用戶代碼的時間/總時間,比如:JVM 運行100分鐘,其中運行用戶代碼99分鐘,垃圾收集1分鐘,則吞吐是99%,這種收集器能最高效率的利用CPU,適合運行後臺運算
2、-XX:UseParallelGC 開啓
使用Parallel Scaveng+Serial Old 收集器組合回收垃圾,這也是在Server 模式下的默認值
3、-XX:GCTimeRatio
來設置用戶執行時間佔總時間的比例,默認99 即1%的時間用來進行垃圾回收
4、-XX:MaxGGFCPauseMilllis
設置GC的最大停頓時間
5、使用複製算法
老年代Parallel Old 回收器
1、-XX:+UseParallel Old 組合收集器進行收集
2、使用標記整理算法
3、並行的、獨佔式的垃圾回收器
******互聯網公司所用
cms (併發標記清楚)回收器
1、標記-清楚算法
同時他又是一個使用多線程併發回收的垃圾收集器
2、-XX:ParallellCMSThreads
手工設定cms 的線程數量,cms 默認啓動線程數是(ParallelGCThreads+3/4)
3、-XX+UseConcMarkSweepGC 開啓
使用ParNew+CMS+Serial Old 的收集器組合進行內存回收,Serial Old 作爲CMS出現“Concurrent Mode Failure”失敗後的後備收集器使用
4、-XX:CMSInitiatingOccupancyFraction
設置CMS 收集器在老年代空間被使用多少後觸發垃圾收集,默認值爲68%,僅在cms收集器時有效-XX:CMSInitiatingOccupancyFraction=70
5、-XX:+UseCMSCompactAtFullCollection
由於CMS 收集器會產生碎片,此參數設置在垃圾收集器後是否需要一次內存碎片整理過程,僅在CMs 收集器有效時有效
6、-XX:+CMSFullGCBeforeCompaction
設置CMS 收集器進行若干次垃圾收集後在進行一次內存碎片整理過程,通常與UserCMSCompactAtFullCollection參數一起使用
7、-XX:+CMSInitiatingPermOccupancyFraction
設置perm Gen 使用到達多少比率時觸發,默認是92%
下面是生產例子:(需要根據實際情況部署)
[root@order bin]# cat setenv.sh #!/bin/sh export JAVA_HOME=/usr/local/jdk1.7.0_60/ export JRE_HOME=/usr/local/jdk1.7.0_60/ JAVA_OPTS="-server -Xms5G ## 堆內存初始值 -Xmx5G ##堆內存最大值 -Xmn2G ##新生代空間 -Dsun.java2d.noddraw=true ##如果硬件加速已經被enable,可以通過這個選項來提高Swing GUI速度,默認值爲false -XX:PermSize=500M ## 表示非堆區初始內存分配大小,其縮寫爲permanent size(持久化內存) -XX:MaxPermSize=500M ##-XX:MaxPermSize:表示對非堆區分配的內存的最大上限。 -Xss256k ##設置每個線程的堆棧大小 -XX:MaxTenuringThreshold=0 ##在新生代中對象存活次數(經過MinorGC次數)後仍然存活,就會晉升到舊生代 -XX:+UseParNewGC ##設置年輕代多線程收集,可與CMS 收集同時使用。在serial 基礎上實現的多線程收集器 -XX:+UseConcMarkSweepGC ##併發標記清楚(CMS)收集器:cms 收集器 也被稱爲短暫停頓併發收集器。它是對年老代進行垃圾收集的。因爲是多線程進行垃圾收回,可與減少停頓時間 -XX:+UseCMSCompactAtFullCollection ## 表示觸發FGC 之後進行壓縮,因爲CMS 默認不壓縮空間的 -XX:CMSFullGCsBeforeCompaction=0 ##,在上一次CMS併發GC執行過後,到底還要再執行多少次full GC纔會做壓縮。默認是0,也就是在默認配置下每次CMS GC頂不住了而要轉入full GC的時候都會做壓縮。 把CMSFullGCsBeforeCompaction配置爲10,就會讓上面說的第一個條件變成每隔10次真正的full GC才做一次壓縮(而不是每10次CMS併發GC就做一次壓縮,目前VM裏沒有這樣的參數)。這會使full GC更少做壓縮,也就更容易使CMS的old gen受碎片化問題的困擾。 -XX:+CMSClassUnloadingEnabled ##如果你啓用了CMSClassUnloadingEnabled ,垃圾回收會清理持久代,移除不再使用的classes。這個參數只有在 UseConcMarkSweepGC 也啓用的情況下才有用 -XX:-CMSParallelRemarkEnabled ##降低標記停頓 表示並行remark -XX:CMSInitiatingOccupancyFraction=90 ##使用cms作爲垃圾回收,使用70%後開始CMS收集,爲了保證不出現promotion failed(見下面介紹)錯誤 -XX:SoftRefLRUPolicyMSPerMB=0 ##在最後一次引用時,軟可達對象將保持一定的時間 等待多少秒 -XX:LargePageSizeInBytes=128M ##設置用於Java堆的大頁面尺寸 -XX:+UseFastAccessorMethods ##原始類型的快速優化 -XX:+UseCMSInitiatingOccupancyOnly ##使用手動定義初始化定義開始CMS收集 -XX:+PrintClassHistogram ##在垃圾收集之前執行 -XX:+PrintGCDetails ##GC 輸出 -XX:+PrintGCTimeStamps ##GC 輸出 -XX:+PrintHeapAtGC ##打印GC前後的詳細堆棧信息 -Xloggc:${CATALINA_HOME}/logs/gc.log" ##把相關日誌信息記錄到文件以便分析.與上面幾個配合使用 |