国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java虛擬機解析篇之---垃圾回收器

2019-11-06 06:16:04
字體:
來源:轉載
供稿:網友
上一篇說了虛擬機的內存模型,在說到堆內存的時候我們提到了,堆內存是java內存中區域最大的一部分,而且垃圾回收器主要就是回收這部分的內容。那么這篇就來介紹一下垃圾回收器的原理以及回收的算法。

Java中的垃圾回收器(GC)是Java中比較有特色的一點,不需要我們手動的去管理一個對象,不想C++中的構造函數和析構函數一樣,需要程序猿自己去手動的管理,很容易造成內存泄露的問題。當然如果學過OC語言的話,我們知道OC語言中有自動釋放池的概念,當然我們使用retain/release進行手動管理對象的。所以從這方面我們可以看到Android(Java)在這方面和iOS(OC)相比的話,比較卡,因為Java中的垃圾回收器是需要算法計算的,這個可能會有點耗時,但是好處就是不需要人工管理。但是OC是需要手動管理的,這樣系統就不需要復雜的算法去進行管理,運行速度就比Android流暢(當然IOS比Android流暢,有很多原因的,這個只是一方面內容)

一、Java中是如何管理對象的

在堆中存放著Java世界中幾乎所有的對象實例,垃圾收集器在對堆進行回收前,第一件事就是要確定這些對象之中哪些還“存活”著,哪些已經“死去”

這里就介紹兩種方式來管理對象

第一、引用技術算法

這種算法很簡單的,而且也是比較常用的一種方式管理對象了

給對象中添加一個引用計數器,每當有一個地方引用它時,計數器的值就加1,當引用失效時,計數器的值就減1,任何時刻計數器為0的對象就是不可能在被使用了,這種算法是很簡單的,而且早期很多面向對象語言中都采用這種方式,但是現在主流的Java虛擬機中并沒有采用這種方式來管理對象,其原因最主要的原因是它很難解決對象之間的相互循環引用。例子:

[java] view plain copy 在CODE上查看代碼片派生到我的代碼片public class Demo {      public Object instance = null;      public static void main(String[] args){          Demo demo1 = new Demo();          Demo demo2 = new Demo();          demo1.instance = demo2;          demo2.instance = demo1;          //現在demo1,demo2設置null,那么demo1,demo2能否被回收呢?          demo1 = null;          demo2 = null;          //手動的調用垃圾回收器回收          System.gc();      }  }  Demo對象中有一個變量instance,當我們new了兩個Demo對象的時候,彼此相關聯,當我們將他們設置null,實際上這兩個對象已經不可能被訪問,但是他們因為互相引用著對方,導致它們的引用計數不為0,于是引用計數算法無法通知GC收集器回收它們。這有點類似于并發中的死鎖。

所以解決上面存在的問題,第二種方式就出現了。

第二、可達性分析算法

這個算法的基本思路就是通過一系列的稱謂“GC Roots"的對象作為起始點,從這些節點開始向下搜索,搜索所有走過的路徑為引用鏈,當一個對象到GC Roots沒有任何引用鏈項鏈時,則證明此對象時不可用的,下面看一下例子:

上面的這張圖,對象object5、object6、object7雖然互相沒有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的對象

注:Java語言中,可作為GC Roots的對象包括下面幾種:

1) 虛擬機棧(棧幀中的本地變量表)中引用的對象

2) 方法區中類靜態屬性引用的對象

3) 方法區中常量引用的對象

4) 本地方法棧中JNI(即一般說的Native方法)引用的對象

二、Java中的引用類型

從JDK1.2之后,Java對引用的概念進行了擴充,將引用分為強引用,軟引用,弱引用,虛引用,這四種引用的強度一次逐漸減弱

1) 強引用就是指在程序代碼之中普遍存在的,類似 “Object obj = new Object()” 這類的引用,只要強引用還存在,垃圾回收器永遠不會回收掉被引用的對象。

2) 軟引用是用來描述一些還有用但并非需要的對象,對于軟引用關聯著的對象,在系統將要發生內存異常之前,將會把這些對象列進回收范圍之中進行第二次回收,如果這次回收還沒有足夠的內存,才會拋出內存異常

3) 弱引用也是用來描述非必需對象的,但是它的強度比軟引用更弱一些,被弱引用關聯的對象只能生存島下一次垃圾收集發生之前,當垃圾收集器工作時,無論當前內存釋放足夠,都會回收掉只被弱引用關聯的對象

4) 虛引用也稱為幽靈引用或者幻影引用,它是最弱的一種引用關系,一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來取得一個對象實例,對一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知

三、垃圾收集算法

1) 標記-清除算法

最基礎的收集算法是“標記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收掉所有被標記的對象,它的標記過程其實在前一節講述對象標記判定時已經基本介紹過了。之所以說它是最基礎的收集算法,是因為后續的收集算法都是基于這種思路并對其缺點進行改進而得到的。它的主要缺點有兩個:一個是效率問題,標記和清除過程的效率都不高;另外一個是空間問題,標記清除之后會產生大量不連續的內存碎片,空間碎片太多可能會導致,當程序在以后的運行過程中需要分配較大對象時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。 標記-清除算法的執行過程如圖

2) 復制算法

為了解決效率問題,一種稱為“復制”(Copying)的收集算法出現了,它將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。這樣使得每次都是對其中的一塊進行內存回收,內存分配時也就不用考慮內存碎片等復雜情況,只要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。只是這種算法的代價是將內存縮小為原來的一半,未免太高了一點。復制算法的執行過程如圖

3) 標記-整理算法

復制收集算法在對象存活率較高時就要執行較多的復制操作,效率將會變低。更關鍵的是,如果不想浪費50%的空間,就需要有額外的空間進行分配擔保,以應對被使用的內存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。根據老年代的特點,有人提出了另外一種“標記-整理”(Mark-Compact)算法,標記過程仍然與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存,“標記-整理”算法的示意圖如圖

4) 分代收集算法

當前商業虛擬機的垃圾收集都采用“分代收集”(Generational Collection)算法,這種算法并沒有什么新的思想,只是根據對象的存活周期的不同將內存劃分為幾塊。一般是把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用復制算法,只需要付出少量存活對象的復制成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記-清理”或“標記-整理”算法來進行回收

四、Java虛擬機對堆的內存細化的幾個區域,并且這些區域都是采用哪些收集算法。

JVM內存模型中分兩大塊,一塊是New Generation, 另一塊是Old Generation. 在New Generation中,有一個叫Eden的空間,主要是用來存放新生的對象,還有兩個Survivor Spaces(from,to), 它們用來存放每次垃圾回收后存活下來的對象。在Old Generation中,主要存放應用程序中生命周期長的內存對象,還有個Permanent Generation,主要用來放JVM自己的反射對象,比如類對象和方法對象等。1) 在New Generation塊中,垃圾回收一般用復制算法,速度快。每次GC的時候,存活下來的對象首先由Eden拷貝到某個Survivor Space, 當Survivor Space空間滿了后, 剩下的live對象就被直接拷貝到Old Generation中去。因此,每次GC后,Eden內存塊會被清空

2) 在Old Generation塊中,垃圾回收一般用標記整理的算法,速度慢些,但減少內存要求.

垃圾回收分多級,0級為全部(Full)的垃圾回收,會回收Old段中的垃圾;1級或以上為部分垃圾回收,只會回收New中的垃圾,內存溢出通常發生于Old段或Perm段垃圾回收后,仍然無內存空間容納新的Java對象的情況。

Out Of Memory 只發生在jvm對old和perm generation 回收后還不能獲足夠內存的情況.當生成一個新對象時,內存申請過程如下:A. JVM會試圖為相關Java對象在Eden中初始化一塊內存區域B. 當Eden空間足夠時,內存申請結束。否則到下一步C. JVM試圖釋放在Eden中所有不活躍的對象(這屬于1或更高級的垃圾回收), 釋放后若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區D. Survivor區被用來作為Eden及Old的中間交換區域,當Old區空間足夠時,Survivor區的對象會被移到Old區,否則會被保留在Survivor區E. 當Old區空間不夠時,JVM會在Old區進行完全的垃圾收集(0級)F. 完全垃圾收集后,若Survivor及Old區仍然無法存放從Eden復制過來的部分對象,導致JVM無法在Eden區為新對象創建內存區域,則出現”out of memory錯誤”

造成full gc的原因new了很多對象,沒有即時在主動釋放掉->Eden內存不夠用->不斷把對象往old遷移->old滿了->full gc

總結:上面的內容就介紹了Java虛擬機如何管理對象的,我們也看到了上面主要就是收集算法和堆空間的從新劃分,這樣做的目的都是在于垃圾回收的高效執行,但是總歸看來,如果對象交給系統來管理,在系統運行的過程效率肯定會有影響的,但是這有一點比較好,就是不需要手動管理,給程序猿帶來方便。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 福安市| 永靖县| 航空| 苏尼特右旗| 婺源县| 江都市| 漳浦县| 稷山县| 光泽县| 宁夏| 西宁市| 东阳市| 北海市| 鹤岗市| 定西市| 北安市| 花莲市| 桦南县| 石台县| 南岸区| 体育| 仁怀市| 吉水县| 灵石县| 琼海市| 浑源县| 葫芦岛市| 乐平市| 广西| 东辽县| 平阳县| 溆浦县| 雅江县| 临江市| 大城县| 张掖市| 巴南区| 长海县| 炉霍县| 黄龙县| 宾川县|