青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

coreBugZJ

此 blog 已棄。

Java 理論與實踐: 垃圾收集簡史 (轉)

垃圾收集的好處是無可爭辯的 ―― 可靠性提高、使內存管理與類接口設計分離,并使開發者減少了跟蹤內存管理錯誤的時間。著名的懸空指針和內存泄漏問題在 Java 程序中再也不會發生了(Java 程序可能會出現某種形式的內存泄漏,更精確地說是非故意的對象保留,但是這是一個不同的問題)。不過,垃圾收集不是沒有代價的 ―― 其中包括對性能的影響、暫停、配置復雜性和不確定的結束 (nondeterministic finalization)。


一個理想的垃圾收集實現應該是完全不可見的 ―― 沒有垃圾收集暫停、沒有因為垃圾收集而產生的 CPU 時間損失、垃圾收集器不會與虛擬內存或者緩存有負面的互動,并且堆不需要大于應用程序的 駐留空間(即堆占用)。當然,沒有十全十美的垃圾收集器,但是垃圾收集器在過去十年中已經有了很大改進。


選項與選擇


1.3 JDK 包括三種不同的垃圾收集策略,1.4.1 JDK 包括六種垃圾收集策略以及 12 種以上用于配置和優化垃圾收集的命令行選項。它們有什么不同?為什么需要有這么多選項?


不同的垃圾收集實現使用不同的策略來識別和收回不可到達的對象,它們與用戶程序和調度器以不同的方式互動。不同類型的應用程序對于垃圾收集有不同的要求 ―― 實時應用程序會將要求收集暫停的持續時間短并且有限制,而企業應用程序可能允許更長時間和可預測性更低的暫停以獲得更高的吞吐能力。



垃圾收集如何工作?


有幾種垃圾收集的基本策略:引用計數、標記-清除、標記-整理 (mark-compact) 和復制。此外,一些算法可以以 增量 方式完成它們的工作(不需要一次收集整個堆,使得收集暫停時間更短),一些算法可以在用戶程序運行時運行( 并發收集)。其他算法則必須在用戶程序暫停時一次進行整個收集(即所謂的 stop-the-world收集器)。最后,還有混合型的收集器,如 1.2 和以后版本的 JDK 使用的分代收集器,它對堆的不同區域使用不同的收集算法。

在對垃圾收集算法進行評價時,我們可能要考慮以下所有標準:

  • 暫停時間。收集器是否停止所有工作來進行垃圾收集?要停止多長時間?暫停是否有時間限制?
  • 暫停的可預測性。垃圾收集暫停是否規劃為在用戶程序方便而不是垃圾收集器方便的時間發生?
  • CPU 占用。總的可用 CPU 時間用在垃圾收集上的百分比是多少?
  • 內存大小。許多垃圾收集算法需要將堆分割成獨立的內存空間,其中一些空間在某些時刻對用戶程序是不可訪問的。這意味著堆的實際大小可能比用戶程序的最大堆駐留空間要大幾倍。
  • 虛擬內存交互。在具有有限物理內存的系統上,一個完整的垃圾收集在垃圾收集過程中可能會錯誤地將非常駐頁面放到內存中來進行檢查。因為頁面錯誤的成本很高,所以垃圾收集器正確管理引用的區域性 (locality) 是很必要的。
  • 緩存交互。即使在整個堆可以放到主內存中的系統上 ―― 實際上幾乎所有 Java 應用程序都可以做到這一點,垃圾收集也常常會有將用戶程序使用的數據沖出緩存的效果,從而影響用戶程序的性能。
  • 對程序區域性的影響。雖然一些人認為垃圾收集器的工作只是收回不可到達的內存,但是其他人認為垃圾收集器還應該盡量改進用戶程序的引用區域性。整理收集器和復制收集器在收集過程中重新安排對象,這有可能改進區域性。
  • 編譯器和運行時影響。一些垃圾收集算法要求編譯器或者運行時環境的重要配合,如當進行指針分配時更新引用計數。這增加了編譯器的工作,因為它必須生成這些簿記指令,同時增加了運行時環境的開銷,因為它必須執行這些額外的指令。這些要求對性能有什么影響呢?它是否會干擾編譯時優化呢?

不管選擇什么算法,硬件和軟件的發展使垃圾收集更具有實用性。20 世紀 70 和 80 年代的經驗研究表明,對于大型 Lisp 程序,垃圾收集消耗 25% 到 40% 的運行時。垃圾收集還不能做到完全不可見,這肯定還有很長的路要走。


基本算法

所有垃圾收集算法所面臨的問題是相同的 ―― 找出由分配器分配的,但是用戶程序不可到達的內存塊。不可到達是什么意思?可以以兩種方式之一訪問內存塊 ―― 或者用戶程序在 根 (root)中有對這一內存塊的引用,或者在另一個可到達的塊中有對這個塊的引用。在 Java 程序中,根是對靜態變量中或者活躍的堆棧框架上的本地變量中所包含的對象的引用。可到達的對象集是指向關系下根集的傳遞閉包。


引用計數

最直觀的垃圾收集策略是引用計數。引用計數很簡單,但是需要編譯器的重要配合,并且增加了賦值 函數 (mutator)的開銷(這個術語是針對用戶程序的,是從垃圾收集器的角度來看的)。每一個對象都有一個關聯的引用計數 ―― 對該對象的活躍引用的數量。如果對象的引用計數是零,那么它就是垃圾(用戶程序不可到達它),并可以回收。每次修改指針引用時(比如通過賦值語句),或者當引用超出范圍時,編譯器必須生成代碼以更新引用的對象的引用計數。如果對象的引用計數變為零,那么運行時就可以立即收回這個塊(并且減少被回收的塊所引用的所有塊的引用計數),或者將它放到遲延收集隊列中。


許多 ANSI C++ 庫類,比如 string ,使用了引用計數來提供垃圾收集的特性。通過重載賦值操作符并利用 C++ 作用域提供的確定性結束,C++ 程序可以將 string 類當成是被收集的垃圾那樣使用。引用計數很簡單,很適用于增量收集,收集過程一般會得到好的引用區域性,但是出于幾個理由,它很少在生產垃圾收集器中使用,如它不能回收不可到達的循環結構(彼此直接或者間接引用的幾個對象,如循環鏈接的列表或者包含指向父節點的反向指針的樹)。


跟蹤收集器

JDK 中的標準垃圾收集器都沒有使用引用計數,相反,它們都使用某種形式的 跟蹤收集器 (tracing collector)。跟蹤收集器停止所有工作(盡管不需要在收集的整個過程中都這樣)并開始跟蹤對象,從根集開始沿著引用跟蹤,直到檢查了所有可到達的對象。可以在程序注冊表中、每一個線程堆棧中的(基于堆棧的)局部變量中以及靜態變量中找到根。


標記-清除收集器

最早由 Lisp 的發明人 John McCarthy 于 1960 年提出的最基本的跟蹤收集器形式是 標記―清除收集器,它停止所有工作,收集器從根開始訪問每一個活躍的節點,標記它所訪問的每一個節點。走過所有引用后,收集就完成了,然后就對堆進行清除(即對堆中的每一個對象進行檢查),所有沒有標記的對象都作為垃圾回收并返回空閑列表。圖 1 展示了垃圾收集之前的堆,陰影塊是垃圾,因為用戶程序不能到達它們:


圖 1. 可到達和不可到達的對象
可到達和不可到達的對象

標記-清除實現起來很簡單,可以容易地回收循環的結構,并且不像引用計數那樣增加編譯器或者賦值函數的負擔。但是它也有不足 ―― 收集暫停可能會很長,在清除階段整個堆都是可訪問的,這對于可能有頁面交換的堆的虛擬內存系統有非常負面的性能影響。


標記-清除的最大問題是,每一個活躍的(即已分配的)對象,不管是不是可到達的,在清除階段都是可以訪問的。因為很多對象都可能成為垃圾,這意思著收集器花費大量精力去檢查并處理垃圾。標記-清除收集器還容易使堆產生碎片,這會產生區域性問題并可以造成分配失敗,即使看來有足夠的自由內存可用。


復制收集器

在另一種形式的跟蹤收集器 ―― 復制收集器中,堆被分成兩個大小相等的半空間,其中一個包含活躍的數據,另一個未使用。當活躍的空間占滿以后,程序就會停止,活躍的對象被從活躍的空間復制到不活躍的空間中。空間的角色就會轉換,原來不活躍的空間成為了新的活躍空間。


復制收集的優點是只訪問活躍的對象,這意味著不會檢查垃圾對象,也不需要將它們頁交換到內存中或者送到緩存中。復制收集器的收集周期時間是由活躍對象的數量決定的。不過,復制收集器因為要將數據從一個空間復制到另一個空間、調整所有引用以指向新備份而增加了成本。特別是,長壽的對象在每次收集時都要來回復制。


堆整理

復制收集器有另一個好處,活躍對象集會被整理到堆的底部。這不僅改進了用戶程序的引用區域性并消除了堆碎片,而且極大地減少了對象分配的成本 ―― 對象分配變成了在堆頂部的指針上增加指針。不需要維護自由列表或者后備列表,或者使用性能最佳或者第一合適的算法 ―― 分配 N 字節就是在堆頂部指針上加 N 并返回前一個值這么簡單,如清單 1 所示:


清單 1. 復制收集器中廉價的內存分配
void *malloc(int n) { 
    if (heapTop - heapStart < n)
        doGarbageCollection();
    void *wasStart = heapStart;
    heapStart += n;
    return wasStart;
}

為非垃圾收集語言實現了復雜內存管理方案的開發人員可能會對復制收集器中廉價的內存分配感到吃驚 ―― 就是指針加法這么簡單。以前的 JVM 實現沒有使用復制收集器 ―― 這可能是對象分配是昂貴的這一想法是如此普遍的原因之一,開發人員仍然下意識地假設分配成本與其他語言(如 C)類似,而事實上在 Java 運行時中可能要廉價得多。不但是分配成本減少了,而且對于在下次收集之前成為垃圾的對象,解除分配的成本為零,因為既不會訪問也不會復制垃圾對象。


標記-整理收集器

復制算法的性能很優異,但是它有一個缺點是需要兩倍于標記-清除收集器所需要的內存。 標記-整理 算法結合了標記-清除和復制,避免了這個問題,代價是增加了一些收集復雜性。與標記-清除類似,標記-整理是兩階段過程,在標記階段訪問并標記每個活躍對象。然后,復制標記的對象,使所有活躍對象被整理到堆的底部。如果每一次收集時進行徹底的整理,那么得到的堆就類似于復制收集器的結果 ―― 在堆的活躍部分與自由部分有明確的界線,這樣分配成本與復制收集器相當。長壽的對象趨向于沉在堆的底部,這樣就不會像在復制收集器中那樣反復復制它們。


選擇哪一種呢?

那么 JDK 使用了哪種方式進行垃圾收集呢?在某種意義上,使用了所有的方式。早期的 JDK 使用了單線程的標記-清除或者標記-清除-整理收集器。1.2 及以后的 JDK 使用了混合的方式,稱為 分代收集,其中根據對象的年齡將堆分為幾個部分,不同的代是用不同的收集算法收集的。


分代收集證明是非常高效的,盡管在運行時它需要更多的簿記。在下一個月的 Java 理論與實踐 中,除了介紹 1.4.1 JVM 提供的所有其他垃圾收集選項之外,我們還將探討分代收集是如何工作的以及 1.4.1 JVM 是如何使用它的。在下下篇文章中,我們將分析垃圾收集對性能的影響,包括揭示與內存管理有關的性能神話。



參考資料

關于作者

Brian Goetz 在過去 15 年間一直從事專業軟件開發。他是 Quiotix 的首席顧問,該公司是一家位于加利福尼亞州洛斯拉圖斯的軟件開發和咨詢公司,他也參與了幾個 JCP 專家組的工作。請參閱流行的業界出版物中 Brian 已經 發表和即將發表的文章。您可以通過 brian@quiotix.com與 Brian 聯系。

posted on 2012-04-01 18:27 coreBugZJ 閱讀(366) 評論(0)  編輯 收藏 引用 所屬分類: 技術視野ProgrammingLanguageLisp

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美成人激情视频| 亚洲精品视频在线观看网站| 亚洲欧美在线高清| 欧美电影在线| 一区二区三区回区在观看免费视频| 久久久久久久久久久久久9999| 欧美日本在线看| 黄色成人在线免费| 久久婷婷久久一区二区三区| 亚洲精品乱码久久久久久日本蜜臀| 久久久久久91香蕉国产| 欧美色图首页| 亚洲国产成人在线视频| 欧美高清视频在线播放| 午夜日本精品| 亚洲欧美视频| 国产一区二区三区的电影 | 136国产福利精品导航网址| 国内在线观看一区二区三区 | 久久久99精品免费观看不卡| 亚洲一区三区电影在线观看| 欧美日韩亚洲综合| 一区二区三区不卡视频在线观看 | 中文国产成人精品| 欧美日韩久久精品| 最新中文字幕亚洲| 亚洲精品女人| 久久久欧美一区二区| 亚洲成人在线视频播放| 久久亚洲精品网站| 欧美一区免费| 亚洲国产成人高清精品| 欧美大片在线观看| 欧美剧在线观看| 一二三四社区欧美黄| 亚洲精品日韩激情在线电影| 国产精品一区二区久激情瑜伽| 亚洲免费在线看| 久久精品系列| 亚洲国产精品va在线看黑人动漫| 亚洲欧洲综合另类在线| 欧美区在线观看| 亚洲男人第一av网站| 久久激情网站| 91久久黄色| 亚洲男人的天堂在线| 国产亚洲人成网站在线观看| 亚洲资源av| 久久亚洲图片| 99视频一区| 欧美主播一区二区三区| 亚洲成人中文| 日韩网站在线观看| 国产资源精品在线观看| 欧美激情1区2区| 国产欧美一区二区精品婷婷 | 亚洲欧美色一区| 最近中文字幕mv在线一区二区三区四区| 亚洲国产高清一区| 国产精品一区久久久| 久久综合九色综合久99| 午夜精品国产更新| 一本色道久久综合亚洲精品小说| 一区二区日韩欧美| 最新亚洲视频| 亚洲欧美日韩另类精品一区二区三区| 欧美精品一区二区高清在线观看| 一区二区三区国产| 艳妇臀荡乳欲伦亚洲一区| 欧美日韩精品高清| 91久久午夜| 亚洲精品一区二区网址| 欧美日韩在线电影| 欧美一区二区在线看| 亚洲一级二级| 久久久精品欧美丰满| 久久天天躁狠狠躁夜夜av| 欧美 日韩 国产在线| 久久久美女艺术照精彩视频福利播放| 精品福利av| 国产欧美日韩亚洲精品| 午夜精品福利电影| 日韩午夜av电影| 99精品福利视频| 欧美一区二区三区在线免费观看| 亚洲一区日韩| 久久免费高清| 欧美一区二区高清| 一区二区三区蜜桃网| 亚洲欧洲在线观看| aⅴ色国产欧美| 亚洲国内精品| 午夜在线电影亚洲一区| 国产综合欧美| 久久精品国产一区二区电影| 亚洲新中文字幕| 国产精品白丝jk黑袜喷水| 欧美激情久久久久久| 国产精品久久久久久久久借妻 | 欧美国产一区视频在线观看| 免费视频一区| 免费观看成人www动漫视频| 久久一区二区视频| 免费亚洲电影在线| 免费精品视频| 亚洲国产精品高清久久久| 欧美一区二区三区四区在线| 亚洲欧洲精品一区二区| 免费日本视频一区| 欧美11—12娇小xxxx| 激情视频一区| 欧美一区在线直播| 免费成人网www| 尤妮丝一区二区裸体视频| 9i看片成人免费高清| 亚洲一区二区成人| 国产精品久久久久久影院8一贰佰| 一区二区黄色| 香蕉久久一区二区不卡无毒影院| 国产精品一区三区| 欧美一区二区三区在线看| 久久久蜜臀国产一区二区| 国产综合香蕉五月婷在线| 久久久成人网| 日韩亚洲在线| 性视频1819p久久| 一区二区亚洲精品| 欧美11—12娇小xxxx| 91久久精品国产91久久性色tv| 亚洲图片在区色| 国产精品区一区二区三| 久久久久久有精品国产| 欧美黄色一级视频| 国自产拍偷拍福利精品免费一| 久久免费偷拍视频| 免费日韩av电影| 国产在线成人| 欧美日韩在线播放一区二区| 亚洲一区二区三区高清不卡| 美女露胸一区二区三区| 亚洲区第一页| 国产欧美日韩高清| 久久亚洲私人国产精品va媚药 | 久久久综合网站| 亚洲一区二区黄色| 国产一区二区三区久久久久久久久 | 欧美日韩精品免费在线观看视频 | 国产精品久久九九| 欧美一进一出视频| 一区二区三区四区在线| 久久久噜噜噜久久人人看| 一区二区三区视频观看| 国产一区二区三区观看| 一区二区三区高清在线观看| 欧美高清在线视频| 亚洲欧美日韩一区二区| 亚洲伦理一区| 国内成人精品2018免费看 | 欧美日本簧片| 免费欧美日韩国产三级电影| 亚洲私人影院| 99精品免费网| 欧美黑人一区二区三区| 国产一区二区三区久久久久久久久| 欧美精品v国产精品v日韩精品| 亚洲看片网站| 亚洲精品无人区| 蜜臀久久99精品久久久画质超高清 | 午夜激情亚洲| 中文一区在线| 亚洲第一福利社区| 久久国产精品久久久久久| 亚洲尤物视频在线| 亚洲精品一级| 国产精品久久久久一区| 欧美日韩99| 久久只精品国产| 麻豆成人综合网| 欧美伊久线香蕉线新在线| 亚洲国产另类精品专区| 欧美高清在线| 久久视频在线视频| 亚洲茄子视频| 亚洲破处大片| 亚洲国产黄色片| 亚洲精品日韩欧美| 一区在线播放| 免费黄网站欧美| 午夜亚洲视频| 亚洲一区在线观看免费观看电影高清| 亚洲区第一页| 亚洲理论在线| 亚洲伦理自拍| 欧美日韩四区| 一本色道**综合亚洲精品蜜桃冫| 欧美诱惑福利视频| 新狼窝色av性久久久久久| 欧美一区二粉嫩精品国产一线天| 亚洲视频免费| 亚洲图片激情小说|