<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                說到 Java 虛擬機不得不提的一個詞就是 “垃圾回收”(GC,Garbage Collection),而垃圾回收的執行速度則影響著整個程序的執行效率,所以我們需要知道更多關于垃圾回收的具體執行細節,以便為我們選擇合適的垃圾回收器提供理論支持。 我們本課時的面試題是,如何判斷一個對象是否“死亡”?垃圾回收的算法有哪些? #### 典型回答 垃圾回收器首先要做的就是,判斷一個對象是存活狀態還是死亡狀態,死亡的對象將會被標識為垃圾數據并等待收集器進行清除。 判斷一個對象是否為死亡狀態的常用算法有兩個:引用計數器算法和可達性分析算法。 引用計數算法(Reference Counting) 屬于垃圾收集器最早的實現算法了,它是指在創建對象時關聯一個與之相對應的計數器,當此對象被使用時加 1,相反銷毀時 -1。當此計數器為 0 時,則表示此對象未使用,可以被垃圾收集器回收。 引用計數算法的優缺點很明顯,其優點是垃圾回收比較及時,實時性比較高,只要對象計數器為 0,則可以直接進行回收操作;而缺點是無法解決循環引用的問題,比如以下代碼: ``` class CustomOne { private CustomTwo two; public CustomTwo getCustomTwo() { return two; } public void setCustomTwo(CustomTwo two) { this.two = two; } } class CustomTwo { private CustomOne one; public CustomOne getCustomOne() { return one; } public void setCustomOne(CustomOne one) { this.one = one; } } public class RefCountingTest { public static void main(String[] args) { CustomOne one = new CustomOne(); CustomTwo two = new CustomTwo(); one.setCustomTwo(two); two.setCustomOne(one); one = null; two = null; } } ``` 即使 one 和 two 都為 null,但因為循環引用的問題,兩個對象都不能被垃圾收集器所回收。 可達性分析算法(Reachability Analysis) 是目前商業系統中所采用的判斷對象死亡的常用算法,它是指從對象的起點(GC Roots)開始向下搜索,如果對象到 GC Roots 沒有任何引用鏈相連時,也就是說此對象到 GC Roots 不可達時,則表示此對象可以被垃圾回收器所回收,如下圖所示: ![](https://img.kancloud.cn/5d/a2/5da23dd52f2a63f54f3a8d004ff2a985_1290x928.png) 當確定了對象的狀態之后(存活還是死亡)接下來就是進行垃圾回收了,垃圾回收的常見算法有以下幾個: * 標記-清除算法; * 標記-復制算法; * 標記-整理算法。 標記-清除(Mark-Sweep)算法屬于最早的垃圾回收算法,它是由標記階段和清除階段構成的。標記階段會給所有的存活對象做上標記,而清除階段會把沒有被標記的死亡對象進行回收。而標記的判斷方法就是前面講的引用計數算法和可達性分析算法。 標記-清除算法的執行流程如下圖所示: ![](https://img.kancloud.cn/93/56/9356294fe62ffa76ceb548d29c62ee6c_832x802.png) 從上圖可以看出,標記-清除算法有一個最大的問題就是會產生內存空間的碎片化問題,也就是說標記-清除算法執行完成之后會產生大量的不連續內存,這樣當程序需要分配一個大對象時,因為沒有足夠的連續內存而導致需要提前觸發一次垃圾回收動作。 標記-復制算法是標記-清除算法的一個升級,使用它可以有效地解決內存碎片化的問題。它是指將內存分為大小相同的兩塊區域,每次只使用其中的一塊區域,這樣在進行垃圾回收時就可以直接將存活的東西復制到新的內存上,然后再把另一塊內存全部清理掉。這樣就不會產生內存碎片的問題了,其執行流程如下圖所示: ![](https://img.kancloud.cn/24/53/2453b405d8c72b15060a2088a6d57414_828x750.png) 標記-復制的算法雖然可以解決內存碎片的問題,但同時也帶來了新的問題。因為需要將內存分為大小相同的兩塊內存,那么內存的實際可用量其實只有原來的一半,這樣此算法導致了內存的可用率大幅降低了。 標記-整理算法的誕生晚于標記-清除算法和標記-復制算法,它也是由兩個階段組成的:標記階段和整理階段。其中標記階段和標記-清除算法的標記階段一樣,不同的是后面的一個階段,標記-整理算法的后一個階段不是直接對內存進行清除,而是把所有存活的對象移動到內存的一端,然后把另一端的所有死亡對象全部清除,執行流程圖如下圖所示: ![](https://img.kancloud.cn/4f/46/4f465adcb98355541223bb4f02385237_830x774.png) #### 考點分析 本題目考察的是關于垃圾收集的一些理論算法問題,都屬于概念性的問題,只要深入理解之后還是挺容易記憶的。和此知識點相關的面試題還有這些: * Java 中可作為 GC Roots 的對象有哪些? * 說一下死亡對象的判斷細節? #### 知識擴展 * [ ] GC Roots 在 Java 中可以作為 GC Roots 的對象,主要包含以下幾個: * 所有被同步鎖持有的對象,比如被 synchronize 持有的對象; * 字符串常量池里的引用(String Table); * 類型為引用類型的靜態變量; * 虛擬機棧中引用對象; * 本地方法棧中的引用對象。 * [ ] 死亡對象判斷 當使用可達性分析判斷一個對象不可達時,并不會直接標識這個對象為死亡狀態,而是先將它標記為“待死亡”狀態再進行一次校驗。校驗的內容就是此對象是否重寫了 finalize() 方法,如果該對象重寫了 finalize() 方法,那么這個對象將會被存入到 F-Queue 隊列中,等待 JVM 的 Finalizer 線程去執行重寫的 finalize() 方法,在這個方法中如果此對象將自己賦值給某個類變量時,則表示此對象已經被引用了。因此不能被標識為死亡狀態,其他情況則會被標識為死亡狀態。 以上流程對應的示例代碼如下: ``` public class FinalizeTest { // 需要狀態判斷的對象 public static FinalizeTest Hook = null; @Override protected void finalize() throws Throwable { super.finalize(); System.out.println("執行了 finalize 方法"); FinalizeTest.Hook = this; } public static void main(String[] args) throws InterruptedException { Hook = new FinalizeTest(); // 卸載對象,第一次執行 finalize() Hook = null; System.gc(); Thread.sleep(500); // 等待 finalize() 執行 if (Hook != null) { System.out.println("存活狀態"); } else { System.out.println("死亡狀態"); } // 卸載對象,與上一次代碼完全相同 Hook = null; System.gc(); Thread.sleep(500); // 等待 finalize() 執行 if (Hook != null) { System.out.println("存活狀態"); } else { System.out.println("死亡狀態"); } } } ``` 上述代碼的執行結果為: ``` 執行了 finalize 方法 存活狀態 死亡狀態 ``` 從結果可以看出,卸載了兩次對象,第一次執行了 finalize() 方法,成功地把自己從待死亡狀態拉了回來;而第二次同樣的代碼卻沒有執行 finalize() 方法,從而被確認為了死亡狀態,這是因為任何對象的 finalize() 方法都只會被系統調用一次。 雖然可以從 finalize() 方法中把自己從死亡狀態“拯救”出來,但是不建議這樣做,因為所有對象的 finalize() 方法只會執行一次。因此同樣的代碼可能產生的結果是不同的,這樣就給程序的執行帶來了很大的不確定性。 #### 小結 本課時講了對象狀態判斷的兩種算法:引用計數算法和可達性分析算法。其中引用計數算法無法解決循環引用的問題,因此對于絕大多數的商業系統來說使用的都是可達性分析算法;同時還講了垃圾回收的三種算法:標記-清除算法、標記-復制算法、標記-整理算法,其中,標記-清除算法會帶來內存碎片的問題,而標記-復制算法會降低內存的利用率。所以,標記-整理算法算是一個不錯的方案。 #### 課后問答 * 1、這個文檔是不是有點問題?上面說的GC roots,在知識擴展,哪些可以作為GC roots對象時,卻是CG root 講師回復: 檢查了,文稿寫的是 GC Roots * 2、GC Roots 的對象: 字符串常量池里的引用 這個怎么理解?字符串常量池不都是一些字符串常量嗎?垃圾回收算法 為什么沒有講到 分代垃圾回收算法呢? 編輯回復: Java 中有兩個不常用的概念——字面量和符號引用,可以配合其他資料看看哈。本專欄會講一些比較常見的知識點,但整個 Java 體系還有很多的內容,可以配合其他資料一起看哦。 * 3、講師你好,1、怎樣判斷對象到 GC Roots 沒有任何引用鏈相連呢?在代碼中是怎樣體現呢?2、垃圾回收的·fullGc是否是判斷沒有相連后才進行回收,而Mingor GC只是用來標記GC的次數嗎?還是?3、強弱軟虛四種引用在分別是怎樣判斷和GC Roots判斷沒有任何相連的呢?困擾了很多,但是看書沒看懂,期待回復 講師回復: 目前主流的虛擬機都是采用 GC Roots Tracing 的算法來實現跟引用查找的,該算法的核心算法是從 GC Roots 對象作為起始點,利用數學中圖論知識,圖中可達對象便是存活對象,而不可達對象則是需要回收的垃圾內存,這個不做 JVM 開發建議了解就可以了。 垃圾回收要看具體的垃圾回收器,可以看下 25 課時回收的執行流程,那個里面有答案哦。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看