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

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>
            国产视频亚洲精品| 免费看黄裸体一级大秀欧美| 欧美激情亚洲国产| 亚洲美女一区| 99re热精品| 国产麻豆日韩| 久久亚洲国产精品一区二区 | 欧美激情偷拍| 欧美精品成人一区二区在线观看| 99成人在线| 亚洲欧美春色| 在线免费观看一区二区三区| 亚洲二区精品| 欧美日韩免费一区二区三区| 欧美影院在线播放| 久久久亚洲高清| 亚洲午夜一区二区三区| 亚洲一区二区免费视频| 狠狠色丁香久久婷婷综合丁香| 欧美大片一区| 国产精品一区二区久久久| 久久婷婷久久一区二区三区| 欧美精品久久久久久久免费观看| 先锋资源久久| 欧美激情视频在线免费观看 欧美视频免费一| 一区二区av| 久久女同互慰一区二区三区| 亚洲激情综合| 亚洲无限av看| 亚洲国产日韩欧美在线99| 一本色道久久综合亚洲二区三区 | 国产欧美日韩中文字幕在线| 欧美福利精品| 国产麻豆精品theporn| 欧美黄免费看| 国内精品国产成人| 中文欧美字幕免费| 日韩视频―中文字幕| 久久国产福利国产秒拍| 亚洲网站在线播放| 欧美成人午夜激情| 久久综合福利| 国产一区二区剧情av在线| 999在线观看精品免费不卡网站| 国内自拍一区| 欧美一区二区视频97| 亚洲欧美一区二区原创| 欧美噜噜久久久xxx| 欧美成人国产| 激情六月综合| 久久福利资源站| 久久精品国产成人| 国产精品日韩在线播放| 日韩小视频在线观看| 亚洲精品国产精品国产自| 久久久久久久欧美精品| 久久激情综合网| 国产日韩在线视频| 亚洲专区一区二区三区| 亚洲一区二区四区| 国产精品国产三级国产普通话蜜臀 | 亚洲精品一区在线| 麻豆精品国产91久久久久久| 蜜桃伊人久久| 亚洲国产导航| 久久中文久久字幕| 欧美18av| 99国产精品久久久| 欧美区高清在线| 一本色道久久精品| 午夜伦理片一区| 国产欧美日韩精品在线| 午夜精品久久久久久久蜜桃app| 午夜一区二区三区不卡视频| 国产视频在线观看一区二区| 欧美诱惑福利视频| 免费在线一区二区| 亚洲精品美女在线观看| 母乳一区在线观看| 亚洲精品乱码久久久久久| 日韩视频在线一区| 国产精品国产自产拍高清av| 欧美亚洲午夜视频在线观看| 久久夜色精品国产噜噜av| 亚洲国产欧美在线| 欧美精品一区二区三区一线天视频 | 欧美视频免费在线观看| 亚洲欧美日韩精品久久奇米色影视| 美玉足脚交一区二区三区图片| 亚洲欧美日韩国产| 激情一区二区| 蜜桃av一区二区| 中文精品视频| 老鸭窝毛片一区二区三区| 亚洲日韩欧美视频| 国产精品久久久久一区二区三区共 | 亚洲激情视频在线观看| 午夜精品理论片| 一区视频在线看| 欧美天天视频| 久久噜噜亚洲综合| 一区二区三区精品视频在线观看| 久久国产精品久久久久久电车| 亚洲成人原创| 国产精品a久久久久| 欧美伊人久久| av成人免费| 欧美成人资源网| 午夜精品一区二区三区四区| 国产一区亚洲| 欧美日韩成人精品| 久久av最新网址| 亚洲一区亚洲| 亚洲精品网站在线播放gif| 久久夜色精品国产欧美乱| 亚洲午夜久久久久久久久电影网| 韩国一区二区三区在线观看| 国产精品萝li| 欧美日韩在线视频首页| 久久久一区二区| 先锋亚洲精品| 亚洲一级网站| 夜夜嗨av色综合久久久综合网| 嫩草伊人久久精品少妇av杨幂| 欧美亚洲免费高清在线观看| 在线综合亚洲| 一区二区欧美在线| 亚洲精品综合久久中文字幕| 尤物九九久久国产精品的分类| 国产欧美91| 国产精品青草综合久久久久99| 欧美精品一区在线播放| 美女国产一区| 米奇777超碰欧美日韩亚洲| 久久九九免费视频| 久久av资源网站| 久久久www成人免费无遮挡大片| 午夜精品久久久久久| 亚洲女人小视频在线观看| 亚洲天堂免费观看| 亚洲小说欧美另类社区| 亚洲无线观看| 香蕉免费一区二区三区在线观看| 在线亚洲欧美| 亚洲综合三区| 欧美一区观看| 久久久久久久久一区二区| 久久婷婷色综合| 免费一级欧美片在线观看| 欧美大片91| 欧美三级电影一区| 国产精品久久久久999| 国产精品av免费在线观看| 国产精品色婷婷久久58| 国产乱码精品1区2区3区| 国产一区二区三区日韩| 在线免费精品视频| 亚洲美女电影在线| 亚洲免费在线电影| 欧美午夜电影完整版| 欧美中文字幕| 久久久噜噜噜久久人人看| 免费一级欧美片在线观看| 欧美日韩国产123区| 国产精品久久久久久久久久免费看 | 久久久久久噜噜噜久久久精品| 久久亚洲私人国产精品va媚药| 欧美成人tv| 国产精品久久久久久久久久三级 | 欧美午夜宅男影院| 国产情人综合久久777777| 一区二区三区我不卡| aa成人免费视频| 久久精品视频在线看| 欧美国产一区视频在线观看| 99国产精品久久久| 久久精品国产2020观看福利| 欧美高清视频在线播放| 国产乱人伦精品一区二区| 亚洲欧洲精品成人久久奇米网| 亚洲午夜在线| 亚洲国产99| 欧美一区二区三区四区夜夜大片| 免费成年人欧美视频| 国产精品视频1区| 亚洲欧洲美洲综合色网| 欧美一区2区三区4区公司二百| 欧美激情小视频| 午夜亚洲一区| 欧美另类综合| 最新69国产成人精品视频免费| 欧美在线观看视频| 亚洲精品网站在线播放gif| 久久久久国产一区二区三区| 国产精品捆绑调教| 99在线热播精品免费| 欧美77777| 久久大逼视频| 国产亚洲一区二区在线观看| 亚洲永久免费视频|