① java有哪些垃圾回收演算法
常用的垃圾回收演算法有:
(1).引用計數演算法:
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器都為0的對象就是不再被使用的,垃圾收集器將回收該對象使用的內存。
引用計數演算法實現簡單,效率很高,微軟的COM技術、ActionScript、Python等都使用了引用計數演算法進行內存管理,但是引用計數演算法對於對象之間相互循環引用問題難以解決,因此java並沒有使用引用計數演算法。
(2).根搜索演算法:
通過一系列的名為「GC Root」的對象作為起點,從這些節點向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則該對象不可達,該對象是不可使用的,垃圾收集器將回收其所佔的內存。
主流的商用程序語言C#、java和Lisp都使用根搜素演算法進行內存管理。
在java語言中,可作為GC Root的對象包括以下幾種對象:
a. java虛擬機棧(棧幀中的本地變數表)中的引用的對象。
b.方法區中的類靜態屬性引用的對象。
c.方法區中的常量引用的對象。
d.本地方法棧中JNI本地方法的引用對象。
java方法區在Sun HotSpot虛擬機中被稱為永久代,很多人認為該部分的內存是不用回收的,java虛擬機規范也沒有對該部分內存的垃圾收集做規定,但是方法區中的廢棄常量和無用的類還是需要回收以保證永久代不會發生內存溢出。
判斷廢棄常量的方法:如果常量池中的某個常量沒有被任何引用所引用,則該常量是廢棄常量。
判斷無用的類:
(1).該類的所有實例都已經被回收,即java堆中不存在該類的實例對象。
(2).載入該類的類載入器已經被回收。
(3).該類所對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射機制訪問該類的方法。
Java中常用的垃圾收集演算法:
(1).標記-清除演算法:
最基礎的垃圾收集演算法,演算法分為「標記」和「清除」兩個階段:首先標記出所有需要回收的對象,在標記完成之後統一回收掉所有被標記的對象。
標記-清除演算法的缺點有兩個:首先,效率問題,標記和清除效率都不高。其次,標記清除之後會產生大量的不連續的內存碎片,空間碎片太多會導致當程序需要為較大對象分配內存時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
(2).復制演算法:
將可用內存按容量分成大小相等的兩塊,每次只使用其中一塊,當這塊內存使用完了,就將還存活的對象復制到另一塊內存上去,然後把使用過的內存空間一次清理掉。這樣使得每次都是對其中一塊內存進行回收,內存分配時不用考慮內存碎片等復雜情況,只需要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。
復制演算法的缺點顯而易見,可使用的內存降為原來一半。
(3).標記-整理演算法:
標記-整理演算法在標記-清除演算法基礎上做了改進,標記階段是相同的標記出所有需要回收的對象,在標記完成之後不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,在移動過程中清理掉可回收的對象,這個過程叫做整理。
標記-整理演算法相比標記-清除演算法的優點是內存被整理以後不會產生大量不連續內存碎片問題。
復制演算法在對象存活率高的情況下就要執行較多的復制操作,效率將會變低,而在對象存活率高的情況下使用標記-整理演算法效率會大大提高。
(4).分代收集演算法:
根據內存中對象的存活周期不同,將內存劃分為幾塊,java的虛擬機中一般把內存劃分為新生代和年老代,當新創建對象時一般在新生代中分配內存空間,當新生代垃圾收集器回收幾次之後仍然存活的對象會被移動到年老代內存中,當大對象在新生代中無法找到足夠的連續內存時也直接在年老代中創建。
② 什麼是Java的垃圾回收機制內部實現原理是什麼
Java中的對象沒有作用域的概念,只有對象的引用才有作用域。在用new創建出一個對象之後,垃圾回收器就會自動監控該對象,並且會辨別是否需要釋放內存,不需要開發者手動釋放,所以可以很好的防止內存泄漏。
JVM的垃圾回收器採用的是一種分代(generational )回收策略,共分為三個代:
1.Young(年輕代)
年 輕代分三個區。一個Eden區,兩個Survivor區。大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區 (兩個中的一個),當這個Survivor區滿時,此區的存活對象將被復制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一 個Survivor區復制過來的並且此時還存活的對象,將被復制「年老區(Tenured)」。需要注意,Survivor的兩個區是對稱的,沒先後關 系,所以同一個區中可能同時存在從Eden復制過來 對象,和從前一個Survivor復制過來的對象,而復制到年老區的只有從第一個Survivor去過來的對象。而且,Survivor區總有一個是空的。
2.Tenured(年老代)
年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象。
3.Perm(持久代)
用 於存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate等, 在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。持久代大小通過-XX:MaxPermSize=進行設置。
③ java中垃圾回收機制的原理
java中垃圾回收機制的原理
推薦一篇文章:
對高性能JAVA代碼之內存管理
更甚者你寫的代碼,GC根本就回收不了,直接系統掛掉。GC是一段程序,不是智能,他只回收他認為的垃圾,而不是回收你認為的垃圾。
GC垃圾回收:
Grabage Collection相信學過JAVA的人都知道這個是什麼意思。但是他是如何工作的呢?
首先,JVM在管理內存的時候對於變數的管理總是分新對象和老對象。新對象也就是開發者new出來的對象,但是由於生命周期短,那麼他佔用的內存並不是馬上釋放,而是被標記為老對象,這個時候該對象還是要存在一段時間。然後由JVM決定他是否是垃圾對象,並進行回收。
所以我們可以知道,垃圾內存並不是用完了馬上就被釋放,所以就會產生內存釋放不及時的現象,從而降低了內存的使用。而當程序浩大的時候。這種現象更為明顯,並且GC的工作也是需要消耗資源的。所以,也就會產生內存浪費。
JVM中的對象生命周期里談內存回收:
對象的生命周期一般分為7個階段:創建階段,應用階段,不可視階段,不可到達階段,可收集階段,終結階段,釋放階段。
創建階段:首先大家看一下,如下兩段代碼:
test1:
for( int i=0; i《10000; i++)
Object obj=new Object();
test2:
Object obj=null;
for( int i=0; i《10000; i++)
obj=new Object();
這兩段代碼都是相同的功能,但是顯然test2的性能要比test1性能要好,內存使用率要高,這是為什麼呢?原因很簡單,test1每次執行for循環都要創建一個Object的臨時對象,但是這些臨時對象由於JVM的GC不能馬上銷毀,所以他們還要存在很長時間,而test2則只是在內存中保存一份對象的引用,而不必創建大量新臨時變數,從而降低了內存的使用。
另外不要對同一個對象初始化多次。例如:
public class A{
private Hashtable table = new Hashtable();
public A(){ table = new Hashtable();
// 這里應該去掉,因為table已經被初始化。
}
這樣就new了兩個Hashtable,但是卻只使用了一個。另外一個則沒有被引用。而被忽略掉。浪費了內存。並且由於進行了兩次new操作。也影響了代碼的執行速度。
應用階段:即該對象至少有一個引用在維護他。
不可視階段:即超出該變數的作用域。這里有一個很好的做法,因為JVM在GC的時候並不是馬上進行回收,而是要判斷對象是否被其他引用在維護。所以,這個時候如果我們在使用完一個對象以後對其obj=null或者obj.doSomething()操作,將其標記為空,可以幫助JVM及時發現這個垃圾對象。
不可到達階段:就是在JVM中找不到對該對象的直接或者間接的引用。
可收集階段,終結階段,釋放階段:此為回收器發現該對象不可到達,finalize方法已經被執行,或者對象空間已被重用的時候。
JAVA的析構方法:
可能不會有人相信,JAVA有析構函數? 是的,有。因為JAVA所有類都繼承至Object類,而finalize就是Object類的一個方法,這個方法在JAVA中就是類似於C++析構函數。一般來說可以通過重載finalize方法的形式才釋放類中對象。如:
public class A{
public Object a;
public A(){ a = new Object ;}
protected void finalize() throws java.lang.Throwable{
a = null; // 標記為空,釋放對象
super.finalize(); // 遞歸調用超類中的finalize方法。
}
}
當然,什麼時候該方法被調用是由JVM來決定的。..。..。..。..。..。..。..。.
一般來說,我們需要創建一個destory的方法來顯式的調用該方法。然後在finalize也對該方法進行調用,實現雙保險的做法。
由於對象的創建是遞歸式的,也就是先調用超級類的構造,然後依次向下遞歸調用構造函數,所以應該避免在類的構造函數中初始化變數,這樣可以避免不必要的創建對象造成不必要的內存消耗。當然這里也就看出來介面的優勢。
數組的創建:
由於數組需要給定一個長度,所以在不確定數據數量的時候經常會創建過大,或過小的數組的現象。造成不必要的內存浪費,所以可以通過軟引用的方式來告訴JVM及時回收該內存。(軟引用,具體查資料)。
例如:
Object obj = new char[10000000000000000];
SoftReference ref = new SoftReference(obj);
共享靜態存儲空間:
我們都知道靜態變數在程序運行期間其內存是共享的,因此有時候為了節約內存工件,將一些變數聲明為靜態變數確實可以起到節約內存空間的作用。但是由於靜態變數生命周期很長,不易被系統回收,所以使用靜態變數要合理,不能盲目的使用。以免適得其反。
因此建議在下面情況下使用:
1,變數所包含的對象體積較大,佔用內存過多。
2,變數所包含對象生命周期較長。
3,變數所包含數據穩定。
4,該類的對象實例有對該變數所包含的對象的共享需求。(也就是說是否需要作為全局變數)。
對象重用與GC:
有的時候,如資料庫操作對象,一般情況下我們都需要在各個不同模塊間使用,所以這樣的對象需要進行重用以提高性能。也有效的避免了反復創建對象引起的性能下降。
一般來說對象池是一個不錯的注意。如下:
public abstarct class ObjectPool{
private Hashtable locked,unlocked;
private long expirationTime;
abstract Object create();
abstract void expire( Object o);
abstract void validate( Object o);
synchronized Object getObject(){。..};
synchronized void freeObject(Object o){。..};
這樣我們就完成了一個對象池,我們可以將通過對應的方法來存取刪除所需對象。來維護這快內存提高內存重用。
當然也可以通過調用System.gc()強制系統進行垃圾回收操作。當然這樣的代價是需要消耗一些cpu資源。
不要提前創建對象:
盡量在需要的時候創建對象,重復的分配,構造對象可能會因為垃圾回收做額外的工作降低性能。
JVM內存參數調優:
強制內存回收對於系統自動的內存回收機制會產生負面影響,會加大系統自動回收的處理時間,所以應該盡量避免顯式使用System.gc(),
JVM的設置可以提高系統的性能。例如:
java -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=8 -Xms512m -Xmx512m
具體可以查看java幫助文檔。我們主要介紹程序設計方面的性能提高。
JAVA程序設計中有關內存管理的其他經驗:
根據JVM內存管理的工作原理,可以通過一些技巧和方式讓JVM做GC處理時更加有效。,從而提高內存使用和縮短GC的執行時間。
1,盡早釋放無用對象的引用。即在不使用對象的引用後設置為空,可以加速GC的工作。(當然如果是返回值。..。.)
2,盡量少用finalize函數,此函數是JAVA給程序員提供的一個釋放對象或資源的機會,但是卻會加大GC工作量。
3,如果需要使用到圖片,可以使用soft應用類型,它可以盡可能將圖片讀入內存而不引起OutOfMemory.
4,注意集合數據類型的數據結構,往往數據結構越復雜,GC工作量更大,處理更復雜。
5,盡量避免在默認構造器(構造函數)中創建,初始化大量的對象。
6,盡量避免強制系統做垃圾回收。會增加系統做垃圾回收的最終時間降低性能。
7,盡量避免顯式申請數組,如果不得不申請數組的話,要盡量准確估算數組大小。
8,如果在做遠程方法調用。要盡量減少傳遞的對象大小。或者使用瞬間值避免不必要數據的傳遞。
9,盡量在合適的情況下使用對象池來提高系統性能減少內存開銷,當然,對象池不能過於龐大,會適得其反.
④ java中的垃圾回收是什麼意思
垃圾回收就是gc(gabage collection)。
java比c++的優點就是多了垃圾回收機制,程序回員不用去答關心垃圾的回收,系統會自動調用去回收內存。
一般我們想回收的時候只需要調用system.gc方法就可以了。系統會自己去調用destroy方法和其他的回收方法釋放內存,節省內存空間。
⑤ java中是怎樣進行垃圾回收的
您好,提問者:
1、Java有專門一個線程負責垃圾回收機制。
2、當一個對象為null時垃圾回收線程會不定期的進行回收。
3、我們可以在程序中使用System.gc來通知JVM進行回收,如下代碼:
publicclassGc_ThreadDemoextendsThread{
//重寫父類的垃圾回收方法
publicvoidrun(){
for(inti=0;i<10;i++){
System.out.println(Thread.currentThread().getName());
}
}
//默認extendsObject
@Override
publicvoidfinalize(){
System.out.println(Thread.currentThread().getName()+"....垃圾回收方法運行");
}
//主方法
publicstaticvoidmain(String[]args){
Gc_ThreadDemogc=newGc_ThreadDemo();
for(inti=0;i<10;i++){
newGc_ThreadDemo();//製造匿名對象,好讓JVM執行垃圾回收方法。
System.gc();//通知JVM執行垃圾回收
}
gc.start();
System.out.println(Thread.currentThread().getName());
}
}
//我們正常編譯:javac-dclassesGc_ThreadDemo.java
//如果要看到JVM線程回收情況,我們可以使用:java-verbose:gc-cpclassesGc_ThreadDemo
⑥ java垃圾回收常用的兩種方法是什麼
java垃圾回收由java虛擬機自己調用,程序員不用管
兩種方法是 System.gc()
Runtime.gc()
Java堆的管理—垃圾回收提到一下幾點,很不錯,或許可以作為寫程序時候的准則:
(1)不要試圖去假定垃圾收集發生的時間,這一切都是未知的。比如,方法中的一個臨時對象在方法調用完畢後就變成了無用對象,這個時候它的內存就可以被釋放。
(2)Java中提供了一些和垃圾收集打交道的類,而且提供了一種強行執行垃圾收集的方法--調用System.gc(),但這同樣是個不確定的方法。Java 中並不保證每次調用該方法就一定能夠啟動垃圾收集,它只不過會向JVM發出這樣一個申請,到底是否真正執行垃圾收集,一切都是個未知數。
(3)挑選適合自己的垃圾收集器。一般來說,如果系統沒有特殊和苛刻的性能要求,可以採用JVM的預設選項。否則可以考慮使用有針對性的垃圾收集器,比如增量收集器就比較適合實時性要求較高的系統之中。系統具有較高的配置,有比較多的閑置資源,可以考慮使用並行標記/清除收集器。
(4)關鍵的也是難把握的問題是內存泄漏。良好的編程習慣和嚴謹的編程態度永遠是最重要的,不要讓自己的一個小錯誤導致內存出現大漏洞。
(5)盡早釋放無用對象的引用。大多數程序員在使用臨時變數的時候,都是讓引用變數在退出活動域(scope)後,自動設置為null,暗示垃圾收集器來收集該對象,還必須注意該引用的對象是否被監聽,如果有,則要去掉監聽器,然後再賦空值。
就是說,對於頻繁申請內存和釋放內存的操作,還是自己控制一下比較好,但是System.gc()的方法不一定適用,最好使用finallize強制執行或者寫自己的finallize方法。