java加快内存回收_java内存管理之垃圾回收及JVM调优
GC(garbage Collector 垃圾收集器)
作用:a、內存的動態分配;b、垃圾回收
注:Java所承諾的自動內存管理主要是針對對象內存的回收和對象內存的分配。
一、垃圾標記
程序計數器、Java虛擬機棧、本地方法棧都是線程私有的,也就是每條線程都擁有這三塊區域,而且會隨著線程的創建而創建,線程的結束而銷毀。那么,垃圾收集器在何時清掃這三塊區域的問題就解決了。
然而,堆和方法區中的內存清理工作就沒那么容易了。 堆和方法區所有線程共享,并且都在JVM啟動時創建,一直得運行到JVM停止時。因此它們沒辦法根據線程的創建而創建、線程的結束而釋放。因此需要垃圾標記。
1)引用計數法
每個對象都有一個計數器,當這個對象被一個變量或另一個對象引用一次,該計數器加一;若該引用失效則計數器減一。當計數器為0時,就認為該對象是無效對象。
2)可達性分析法(也叫根搜索算法)
所有和GC Roots直接或間接關聯的對象都是有效對象,和GC Roots沒有關聯的對象就是無效對象。
GC Roots是指
Java虛擬機棧所引用的對象(棧幀中局部變量表中引用類型的變量所引用的對象);
本地方法棧所引用的對象 ;
方法區中靜態屬性引用的對象;
方法區中常量所引用的對象(即常量池中的對象引用)
注意:GC Roots并不包括堆中對象所引用的對象!這樣就不會出現循環引用。
引用計數法雖然簡單,但存在一個嚴重的問題,它無法解決循環引用的問題。?因此,目前主流語言均使用可達性分析方法來判斷對象是否有效。
二、回收無效對象的過程
Object類的finalize()方法,即所有類都有這個方法,如果子類重寫(即覆蓋)了該方法,就會在垃圾回收之前不是直接回收,而是去執行它。即當JVM篩選出失效的對象之后,并不是立即清除,而是再給對象一次重生的機會,具體過程如下:
1,判斷該對象是否覆蓋了finalize()方法
若已覆蓋該方法,并該對象的finalize()方法還沒有被執行過,那么就會將finalize()扔到F-Queue隊列中;若未覆蓋該方法,則釋放對象內存。
2,執行F-Queue隊列中的finalize()方法
虛擬機會以較低的優先級執行這些finalize()方法們,也不會確保所有的finalize()方法都會執行結束。如果finalize()方法中出現耗時操作,虛擬機就直接停止執行,將該對象清除
3,對象重生或死亡
如果在執行finalize()方法時,將this賦給了某一個引用,那么該對象就重生了。如果沒有,那么就會被垃圾收集器清除。
強烈不建議使用finalize()函數進行任何操作!如果需要釋放資源,請使用try-finally。因為finalize()不確定性大,開銷大,無法保證順利執行。
三、垃圾回收算法
現在我們知道了判定一個對象是無效對象、判定一個類是廢棄類、判定一個常量是廢棄常量的方法,也就是知道了垃圾收集器會清除哪些數據,那么接下來介紹如何清除這些數據。
分代收集算法
將內存劃分為老年代和新生代。老年代中存放壽命較長的對象,新生代中存放“朝生夕死”的對象。然后在不同的區域使用不同的垃圾收集算法,如下:
1)標記-清除算法
首先利用剛才介紹的方法判斷需要清除哪些數據,并給它們做上標記;然后清除被標記的數據。
分析:
這種算法標記和清除過程效率都很低,而且清除完后存在大量碎片空間,導致無法存儲大對象,降低了空間利用率。
2.)復制算法(用于新生代)
將內存分成兩份,只將數據存儲在其中一塊上。當需要回收垃圾時,也是首先標記出廢棄的數據,然后將有用的數據復制到另一塊內存上,最后將第一塊內存全部清除。
分析:
這種算法避免了碎片空間,但內存被縮小了一半。
而且每次都需要將有用的數據全部復制到另一片內存上去,效率不高。
解決空間利用率問題:
在新生代中,由于大量的對象都是“朝生夕死”,也就是一次垃圾收集后只有少量對象存活,因此我們可以將內存劃分成三塊:Eden、Survior1、Survior2,內存大小分別是8:1:1。分配內存時,只使用Eden和一塊Survior1。當發現Eden+Survior1的內存即將滿時,JVM會發起一次MinorGC,清除掉廢棄的對象,并將所有存活下來的對象復制到另一塊Survior2中。那么,接下來就使用Survior2+Eden進行內存分配。
通過這種方式,只需要浪費10%的內存空間即可實現帶有壓縮功能的垃圾收集方法,避免了內存碎片的問題。
但是,當一個對象要申請內存空間時,發現Eden+Survior中剩下的空間無法放置該對象,此時需要進行Minor GC,如果MinorGC過后空閑出來的內存空間仍然無法放置該對象,那么此時就需要將對象轉移到老年代中,這種方式叫做“分配擔?!?。
什么是分配擔保?
當JVM準備為一個對象分配內存空間時,發現此時Eden+Survior中空閑的區域無法裝下該對象,那么就會觸發MinorGC,對該區域的廢棄對象進行回收。但如果MinorGC過后只有少量對象被回收,仍然無法裝下新對象,那么此時需要將 Eden+Survior 中的所有對象都轉移到老年代中,然后再將新對象存入Eden區。這個過程就是“分配擔?!?。
3)標記-整理算法(用于老年代)
在回收垃圾前,首先將所有廢棄的對象做上標記,然后將所有未被標記的對象移到一邊,最后清空另一邊區域即可。
分析:
它是一種老年代的垃圾收集算法。老年代中的對象一般壽命比較長,因此每次垃圾回收會有大量對象存活,因此如果選用“復制”算法,每次需要復制大量存活的對象,會導致效率很低。而且,在新生代中使用“復制”算法,當Eden+Survior中都裝不下某個對象時,可以使用老年代的內存進行“分配擔保”,而如果在老年代使用該算法,那么在老年代中如果出現 Eden+Survior 裝不下某個對象時,沒有其他區域給他作分配擔保。因此,老年代中一般使用“標記-整理”算法。
JVM調優
1,減少Full GC
因為Full GC要整理整個堆區,會很慢(幾秒甚至數十秒),因此應該讓盡量少的對象進入老年代。
1)確保對象都是“朝生夕死”的
一個對象使用完后應盡快讓他失效,然后盡快在新生代中被Minor GC回收掉,盡量避免對象在新生代中停留太長時間。
2)提高大對象直接進入老年代的門檻
通過設置參數-XX:PretrnureSizeThreshold來提高大對象的門檻,盡量讓對象都先進入新生代,然后盡快被Minor GC回收掉,而不要直接進入老年代。
2,當高并發時
應該適當增加S區的大小,因為發生YGC時,大量對象(高并發時)被引用而無法被GC掉,S區太小則只能到老年代;
當然增加s區自然會減小Eden區,從而增加YGC的頻率,但是換來的卻是高吞吐量。
默認:
新生代占jvm內存 3/8;
新生代中Eden區:s區:s區=8:1:1
總結
以上是生活随笔為你收集整理的java加快内存回收_java内存管理之垃圾回收及JVM调优的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java icache_Java ICa
- 下一篇: java活动安排_贪心法求解活动安排(j