垃圾回收機制算法
垃圾回收機制概述
Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程序員最頭疼的內存管理的問題迎刃而解诞外,它使得Java程序員在編寫程序的時候不再需要考慮內存管理办桨。由于有個垃圾回收機制,Java中的對象不再有“作用域”的概念,只有對象的引用才有“作用域”疾层。垃圾回收可以有效的防止內存泄露,有效的使用空閑的內存贡避。
ps:內存泄露是指該內存空間使用完畢之后未回收痛黎,在不涉及復雜數據結構的一般情況下,Java 的內存泄露表現為一個內存對象的生命周期超出了程序需要它的時間長度刮吧,我們有時也將其稱為“對象游離”湖饱。
垃圾回收簡要過程
??這里必須點出一個很重要的誤區(qū):不可達的對象并不會馬上就會被直接回收,而是至少要經過兩次標記的過程杀捻。?????????第一次被標記過的對象井厌,會檢查該對象是否重寫了finalize()方法。如果重寫了該方法致讥,則將其放入一個F-Query隊列中仅仆,否則,直接將對象加入“即將回收”集合垢袱。在第二次標記之前墓拜,F-Query隊列中的所有對象會逐個執(zhí)行finalize()方法,但是不保證該隊列中所有對象的finalize()方法都能被執(zhí)行请契,這是因為JVM創(chuàng)建一個低優(yōu)先級的線程去運行此隊列中的方法咳榜,很可能在沒有遍歷完之前,就已經被剝奪了運行的權利爽锥。那么運行finalize()方法的意義何在呢涌韩?這是對象避免自己被清理的最后手段:如果在執(zhí)行finalize()方法的過程中,使得此對象重新與GC Roots引用鏈相連氯夷,則會在第二次標記過程中將此對象從F-Query隊列中清除臣樱,避免在這次回收中被清除,恢復成了一個“正常”的對象雇毫。但顯然這種好事不能無限的發(fā)生奢啥,對于曾經執(zhí)行過一次finalize()的對象來說,之后如果再被標記嘴拢,則不會再執(zhí)行finalize()方法桩盲,只能等待被清除的命運。?????????之后席吴,GC將對F-Queue中的對象進行第二次小規(guī)模的標記赌结,將隊列中重新與GC Roots引用鏈恢復連接的對象清除出“即將回收”集合。所有此集合中的內容將被回收孝冒。
手動GC回收
public?class?JVMDemo05 {
public?static?void?main(String[] args) {
JVMDemo05 jvmDemo05?= new?JVMDemo05();
//jvmDemo05 = null;
System.gc();
}
protected?void?finalize() throws?Throwable {
???????System.out.println("gc在回收對象...");
}
}
finalize作用
Java技術使用finalize()方法在垃圾收集器將對象從內存中清除出去前柬姚,做必要的清理工作。這個方法是由垃圾收集器在確定這個對象沒有被引用時對這個對象調用的庄涡。它是在Object類中定義的量承,因此所有的類都繼承了它。子類覆蓋finalize()方法以整理系統(tǒng)資源或者執(zhí)行其他清理工作穴店。finalize()方法是在垃圾收集器刪除對象之前對這個對象調用的撕捍。
?
垃圾回收機制算法
引用計數法
1.1概述
給對象中添加一個引用計數器,每當有一個地方引用它時泣洞,計數器值就加1忧风;當引用失效時,計數器值就減1球凰;任何時刻計數器都為0的對象就是不再被使用的狮腿,垃圾收集器將回收該對象使用的內存。
1.2優(yōu)缺點
優(yōu)點:
引用計數收集器可以很快的執(zhí)行呕诉,交織在程序運行中缘厢。對程序需要不被長時間打斷的實時環(huán)境比較有利。
缺點:
無法檢測出循環(huán)引用甩挫。如父對象有一個對子對象的引用贴硫,子對象反過來引用父對象。這樣捶闸,他們的引用計數永遠不可能為0.而且每次加減非常浪費內存夜畴。
標記清除算法
標記-清除(Mark-Sweep)算法顧名思義拖刃,主要就是兩個動作删壮,一個是標記,另一個就是清除兑牡。
標記就是根據特定的算法(如:引用計數算法央碟,可達性分析算法等)標出內存中哪些對象可以回收,哪些對象還要繼續(xù)用。
標記指示回收亿虽,那就直接收掉菱涤;標記指示對象還能用,那就原地不動留下洛勉。
缺點
[if !supportLists]1.?[endif]標記與清除效率低;
[if !supportLists]2.?[endif]清除之后內存會產生大量碎片粘秆;
所以碎片這個問題還得處理,怎么處理收毫,看標記-整理算法攻走。
復制算法
S0和s1將可用內存按容量分成大小相等的兩塊,每次只使用其中一塊此再,當這塊內存使用完了昔搂,就將還存活的對象復制到另一塊內存上去,然后把使用過的內存空間一次清理掉输拇。這樣使得每次都是對其中一塊內存進行回收摘符,內存分配時不用考慮內存碎片等復雜情況,只需要移動堆頂指針策吠,按順序分配內存即可逛裤,實現簡單,運行高效猴抹。
復制算法的缺點顯而易見别凹,可使用的內存降為原來一半。
復制算法用于在新生代垃圾回收
標記-壓縮算法
標記壓縮法在標記清除基礎之上做了優(yōu)化洽糟,把存活的對象壓縮到內存一端,而后進行垃圾清理炉菲。(java中老年代使用的就是標記壓縮法)
分代收集算法
根據內存中對象的存活周期不同,將內存劃分為幾塊坤溃,java的虛擬機中一般把內存劃分為新生代和年老代拍霜,當新創(chuàng)建對象時一般在新生代中分配內存空間,當新生代垃圾收集器回收幾次之后仍然存活的對象會被移動到年老代內存中薪介,當大對象在新生代中無法找到足夠的連續(xù)內存時也直接在年老代中創(chuàng)建祠饺。
對于新生代和老年代來說,新生代回收頻率很高,但是每次回收耗時很短,而老年代回收頻率較低,但是耗時會相對較長,所以應該盡量減少老年代的GC.
為什么老年代使用標記壓縮、新生代使用復制算法汁政。
垃圾回收時的停頓現象
垃圾回收的任務是識別和回收垃圾對象進行內存清理道偷,為了讓垃圾回收器可以更高效的執(zhí)行,大部分情況下记劈,會要求系統(tǒng)進入一個停頓的狀態(tài)勺鸦。停頓的目的是為了終止所有的應用線程,只有這樣的系統(tǒng)才不會有新垃圾的產生目木。同時停頓保證了系統(tǒng)狀態(tài)在某一個瞬間的一致性换途,也有利于更好的標記垃圾對象。因此在垃圾回收時,都會產生應用程序的停頓军拟。
垃圾收集器
什么是Java垃圾回收器
Java垃圾回收器是Java虛擬機(JVM)的三個重要模塊(另外兩個是解釋器和多線程機制)之一剃执,為應用程序提供內存的自動分配(Memory Allocation)、自動回收(Garbage Collect)功能懈息,這兩個操作都發(fā)生在Java堆上(一段內存塊)肾档。某一個時點,一個對象如果有一個以上的引用(Rreference)指向它辫继,那么該對象就為活著的(Live)阁最,否則死亡(Dead),視為垃圾骇两,可被垃圾回收器回收再利用速种。垃圾回收操作需要消耗CPU、線程低千、時間等資源配阵,所以容易理解的是垃圾回收操作不是實時的發(fā)生(對象死亡馬上釋放),當內存消耗完或者是達到某一個指標(Threshold,使用內存占總內存的比列示血,比如0.75)時棋傍,觸發(fā)垃圾回收操作。有一個對象死亡的例外难审,java.lang.Thread類型的對象即使沒有引用瘫拣,只要線程還在運行,就不會被回收告喊。
串行回收器(Serial Collector)
單線程執(zhí)行回收操作麸拄,回收期間暫停所有應用線程的執(zhí)行,client模式下的默認回收器黔姜,通過-XX:+UseSerialGC命令行可選項強制指定拢切。參數可以設置使用新生代串行和老年代串行回收器
年輕代的回收算法(Minor Collection)把Eden區(qū)的存活對象移到To區(qū),To區(qū)裝不下直接移到年老代秆吵,把From區(qū)的移到To區(qū)淮椰,To區(qū)裝不下直接移到年老代,From區(qū)里面年齡很大的升級到年老代纳寂。 回收結束之后主穗,Eden和From區(qū)都為空,此時把From和To的功能互換毙芜,From變To忽媒,To變From,每一輪回收之前To都是空的爷肝。設計的選型為復制猾浦。
年老代的回收算法(Full Collection)年老代的回收分為三個步驟陆错,標記(Mark)灯抛、清除(Sweep)金赦、合并(Compact)。標記階段把所有存活的對象標記出來对嚼,清除階段釋放所有死亡的對象夹抗,合并階段 把所有活著的對象合并到年老代的前部分,把空閑的片段都留到后面纵竖。設計的選型為合并漠烧,減少內存的碎片。
并行回收
并行回收器(ParNew回收器)
并行回收器在串行回收器基礎上做了改進靡砌,他可以使用多個線程同時進行垃圾回收已脓,對于計算能力強的計算機而言,可以有效的縮短垃圾回收所需的尖際時間通殃。ParNew回收器是一個工作在新生代的垃圾收集器度液,他只是簡單的將串行回收器多線程快他的回收策略和算法和串行回收器一樣。使用XX:+UseParNewGC?新生代ParNew回收器画舌,老年代則使用市行回收器ParNew回收器工作時的線程數量可以使用XX:ParaleiGCThreads參數指定堕担,一般最好和計算機的CPU相當,避免過多的栽程影響性能曲聂。
并行回收集器(ParallelGC)
老年代ParallelOldGC回收器也是一種多線程的回收器霹购,和新生代的ParallelGC回收器一樣,也是一種關往吞吐量的回收器朋腋,他使用了標記壓縮算法進行實現齐疙。-XX:+UseParallelOldGC?進行設置-XX:+ParallelCThread也可以設置垃圾收集時的線程數量。
?
并CMS(并發(fā)GC)收集? 器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器旭咽。CMS收集器是基于“標記-清除”算法實現的剂碴,整個收集過程大致分為4個步驟:
①.初始標記(CMS initial mark)
②.并發(fā)標記(CMS concurrenr mark)
③.重新標記(CMS remark)
④.并發(fā)清除(CMS concurrent sweep)
其中初始標記、重新標記這兩個步驟任然需要停頓其他用戶線程轻专。初始標記僅僅只是標記出GC ROOTS能直接關聯到的對象忆矛,速度很快,并發(fā)標記階段是進行GC ROOTS 根搜索算法階段请垛,會判定對象是否存活催训。而重新標記階段則是為了修正并發(fā)標記期間,因用戶程序繼續(xù)運行而導致標記產生變動的那一部分對象的標記記錄宗收,這個階段的停頓時間會被初始標記階段稍長漫拭,但比并發(fā)標記階段要短。
由于整個過程中耗時最長的并發(fā)標記和并發(fā)清除過程中混稽,收集器線程都可以與用戶線程一起工作采驻,所以整體來說审胚,CMS收集器的內存回收過程是與用戶線程一起并發(fā)執(zhí)行的。
CMS收集器的優(yōu)點:并發(fā)收集礼旅、低停頓膳叨,但是CMS還遠遠達不到完美,其主要有三個顯著缺點:
CMS收集器對CPU資源非常敏感痘系。在并發(fā)階段菲嘴,雖然不會導致用戶線程停頓,但是會占用CPU資源而導致引用程序變慢汰翠,總吞吐量下降龄坪。CMS默認啟動的回收線程數是:(CPU數量+3) / 4。
CMS收集器無法處理浮動垃圾复唤,可能出現“Concurrent Mode Failure“健田,失敗后而導致另一次Full ?GC的產生。由于CMS并發(fā)清理階段用戶線程還在運行佛纫,伴隨程序的運行自熱會有新的垃圾不斷產生妓局,這一部分垃圾出現在標記過程之后,CMS無法在本次收集中處理它們雳旅,只好留待下一次GC時將其清理掉跟磨。這一部分垃圾稱為“浮動垃圾”。也是由于在垃圾收集階段用戶線程還需要運行攒盈,即需要預留足夠的內存空間給用戶線程使用抵拘,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分內存空間提供并發(fā)收集時的程序運作使用型豁。在默認設置下僵蛛,CMS收集器在老年代使用了68%的空間時就會被激活,也可以通過參數-XX:CMSInitiatingOccupancyFraction的值來提供觸發(fā)百分比迎变,以降低內存回收次數提高性能充尉。要是CMS運行期間預留的內存無法滿足程序其他線程需要,就會出現“Concurrent Mode Failure”失敗衣形,這時候虛擬機將啟動后備預案:臨時啟用Serial Old收集器來重新進行老年代的垃圾收集驼侠,這樣停頓時間就很長了。所以說參數-XX:CMSInitiatingOccupancyFraction設置的過高將會很容易導致“Concurrent Mode Failure”失敗谆吴,性能反而降低倒源。
最后一個缺點,CMS是基于“標記-清除”算法實現的收集器句狼,使用“標記-清除”算法收集后笋熬,會產生大量碎片∧骞剑空間碎片太多時胳螟,將會給對象分配帶來很多麻煩昔馋,比如說大對象,內存空間找不到連續(xù)的空間來分配不得不提前觸發(fā)一次Full ?GC糖耸。為了解決這個問題秘遏,CMS收集器提供了一個-XX:UseCMSCompactAtFullCollection開關參數,用于在Full ?GC之后增加一個碎片整理過程蔬捷,還可通過-XX:CMSFullGCBeforeCompaction參數設置執(zhí)行多少次不壓縮的Full ?GC之后垄提,跟著來一次碎片整理過程榔袋。
G1回收器
G1回收器(Garbage-First)實在]dk1.7中提出的垃圾回收器周拐,從長期目標來看是為了取代CMS回收器,G1回收器擁有獨特的垃圾回收策略凰兑,G1屬于分代垃圾回收器妥粟,區(qū)分新生代和老年代,依然有eden和from/to區(qū),它并不要求整個eden區(qū)或者新生代吏够、老年代的空間都連續(xù)勾给,它使用了分區(qū)算法。并行性:?G1回收期間可多線程同時工作锅知。井發(fā)性G1擁有與應用程序交替執(zhí)行能力播急,部分工作可與應用程序同時執(zhí)行,在整個GC期間不會完全阻塞應用程序售睹。分代GC:G1依然是一個分代的收集器桩警,但是它是非兩新生代和老年代一杯政的雜尊〔茫空間基理捶枢,G1在回收過程中,不會為CMS那樣在若千tacAy?要進行碎片整理飞崖。G1來用了有效復制對象的方式烂叔,減少空間碎片。利得程固歪,用于分區(qū)的原因蒜鸡,G可以貝造取都分區(qū)城進行回收,帽小了國收的格想牢裳,提升了性能逢防。使用.XXX:+UseG1GC?應用G1收集器,Mills指定最大停頓時間使用-XX:MaxGCPausel設置并行回收的線程數量使用-XX:ParallelGCThreads
擴展:
zgc是java1.11中新提出的一種垃圾回收器贰健,它適用于64位機器的大內存的垃圾回收胞四。
什么場景下適合用G1垃圾回收器?
1.50%以上的堆被存活對象占用
2.對象分配和晉升的速度變化非常大
3.垃圾回收時間特別長伶椿,超過1秒
4.8GB以上的堆內存(建議值)
5.停頓時間是500ms以內
Tomcat配置調優(yōu)測試
Jmeter壓力測試工具
JMeter是一款在國外非常流行和受歡迎的開源性能測試工具辜伟,像LoadRunner 一樣氓侧,它也提供了一個利用本地Proxy Server(代理服務器)來錄制生成測試腳本的功能,但是這個功能并不好用导狡。所以在本文中介紹一個更為常用的方法——使用Badboy錄制生成?JMeter?腳本约巷。
簡單的介紹一下Badboy。Badboy是一款不錯的Web自動化測試工具旱捧,如果你將它用于非商業(yè)用途独郎,或者用于商業(yè)用途但是安裝Badboy?的機器數量不超過5臺,你是不需要為它支付任何費用的枚赡。也許是一種推廣策略氓癌,Badboy提供了將Web測試腳本直接導出生成JMeter?腳本的功能,并且這個功能非常好用贫橙,也非常簡單贪婉。你可以跟著下面的試驗步驟來邁出你在開源世界的第一步。
1.????? 通過Badboy的官方網站下載Badboy的最新版本卢肃;
2.????? 安裝Badboy疲迂。安裝過程同一般的Windows 應用程序沒有什么區(qū)別,安裝完成后你可以在桌面和Windows開始菜單中看到相應的快捷方式——如果找不到莫湘,可以找一下Badboy安裝目錄下的Badboy.exe 文件尤蒿,直接雙擊啟動Badboy;
3.????? 啟動Badboy幅垮,你可以看到下面的界面腰池。
在地址欄(圖中紅色方框標注的部分)中輸入你需要錄制的Web應用的URL——這里我們以http://www.yahoo.com?為例,并點擊GO 按鈕開始錄制军洼。如果你用過LoadRunner之類的商業(yè)工具巩螃,對于這個操作一定不會陌生吧^_^
4.????? 開始錄制后,你可以直接在Badboy內嵌的瀏覽器(主界面的右側)中對被測應用進行操作匕争,所有的操作都會被記錄在主界面左側的編輯窗口中——在這個試驗中避乏,我們在Yahoo的搜索引擎中輸入?JMeter?進行搜索。不過你將看到甘桑,錄制下來的腳本并不是一行行的代碼拍皮,而是一個個Web對象——這就有點像LoadRunner的VuGen中的Tree View視圖;
5.????? 錄制完成后跑杭,點擊工具欄中的“停止錄制”按鈕铆帽,完成腳本的錄制;
6.????? 選擇“File -> Export to JMeter”菜單德谅,填寫文件名“login_mantis.jmx”爹橱,將錄制好腳本導出為JMeter腳本格式。也可以選擇“File -> Save”菜單保存為Badboy腳本窄做;
7.????? 啟動JMeter并打開剛剛生成的測試腳本愧驱。
也許你已經急不可待的準備開始嘗試著用JMeter處理你手頭的工作了^_^ 在下面的幾節(jié)慰技,我將繼續(xù)為大家介紹如何在?JMeter?中完成一個測試場景的設置和JMeter測試結果分析入門,以及如何參數化JMeter腳本组砚。
當然吻商,如果你的動手能力很強,幾分鐘你就可以熟悉這些內容糟红。不過還是請允許我一點點由淺入深的來幫大家完成“JMeter從入門到精通”的過程艾帐。我相信在這個過程中你將會了解到更多有關性能測試的知識和經驗,甚至包括一些LoadRunner等商業(yè)測試工具所無法提供給你的經驗盆偿。
測試串行吞吐量
-XX:+PrintGCDetails -Xmx32M -Xms32M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC回收6次 ?吞吐量301
擴大堆的內存
-XX:+PrintGCDetails -Xmx512M –Xms32M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC回收2次 ?吞吐量349
結論 最大內存越大柒爸,吞吐量越高。
調整初始堆
-XX:+PrintGCDetails -Xmx512M –Xms256M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseSerialGC
-XX:PermSize=32M
GC回收0次 ?吞吐量419
并行回收(UseParNewGC)
-XX:+PrintGCDetails -Xmx512M –Xms256M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParNewGC
-XX:PermSize=32M
GC回收0次 吞吐量532
測試結果1794 179
并行合并回收(UseParallelGC)
-XX:+PrintGCDetails -Xmx512M -Xms256M
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:ParallelGCThreads=8
-XX:PermSize=32M
測試結果2100 209
什么是棧溢出
如果想要棧溢出陈肛,可以遞歸調用方法揍鸟,這樣隨著棧深度的增加兄裂,JVM維持著一條長長的方法調用軌跡句旱,
?