日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java垃圾回收总结

發布時間:2025/3/20 java 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java垃圾回收总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

基本概念

垃圾回收器(Garbage Collector )是JVM非常重要的一個組成部分,主要用于自動化的內存管理。相比手動的內存管理,自動化的內存管理大大簡化了程序員的開發難度并且更加安全,避免了各種如內存泄露,懸空引用等問題。GC職責是:分配內存,確保被可達的對象保留在內存中,將不可達的對象的內存進行回收利用。尋找和回收不可達對象的過程被稱為垃圾回收,這些操作都都是為了維護一個大的內存池(通常叫堆)。GC在分配內存時候的難點在于如何能快速在堆中找到一塊未被使用的合適大小的空間(在多線程環境下更加復雜),而在回收的時候要考慮內存碎片化的問題。同時,GC本身也是一個耗費時間和資源的操作,特別是在堆分配的過大的時候,雖然減少了GC的次數,但是執行一次GC可能要耗費很長的時間,如何減少對應用程序的性能影響也是一個重要考量。

可達對象(存活對象)

可達對象是GC識別出來的不能回收的對象,所有被GC Root直接或者間接引用的對象都是可達對象。

STW (Stop-The-World)

Stop-The-World是指在進行垃圾回收的時候,將Java應用程序進行完全停頓,顯然這對應用程序的執行影響比較大。到目前為止,所有的垃圾回收都需要STW,即使是并發收集器也有些階段需要STW,只需時間比較短,Java垃圾收集器不斷改進都是為了降低STW的時間。

快速內存分配

在一塊大而連續的內存上進行內存分配是非常快速的,只需要使用指針碰撞技術即可快速獲得一塊可用內存。在多線程情況下,內存分配必須保證是線程安全的,如果使用加鎖類來處理的話,性能顯然會比較底下。

HotSpot使用了Thread-Local Allocation Buffers (TLABs)技術來進行內存分配,也就是為每個線程分配一個Buffer,每個線程都在自己的Buffer上進行內存分配,這樣就可以在不加鎖的情況下使用指針碰撞來分配內存了,只有在TLAB滿了以后才需要加鎖以獲取一個新的TLAB。TLAB技術可能會帶來一定的內存浪費,但是控制在1%的Eden空間以下。

內存碎片(壓縮,不壓縮,復制)

當垃圾收集器確認內存中哪些是存活對象,哪些是需要回收的對象后,它可以對內存進行壓縮,將所有存活對象移動到連續的內存中,剩下的連續內存就是可用內存了。進行壓縮后內存分配效率將提升,分配一塊內存只需要將一個指針移動到下一個有效內存的起始點。不壓縮的情況是針對需要收集的對象內存做一個標記清除而不進行任何位置調整,這樣就使內存中產生了許多空洞,也就是內存碎片了,導致內存分配比較耗時,因為可能需要搜索整個堆以找到一個可用的空洞。還有一種基于復制的算法,復制操作需要額外的內存空間和時間,但是解決了內存碎片的問題。

分代收集

分帶收集是指將內存分成幾個代,不同的代存放不同年齡的對象,通常分成兩個代:青年代和老年代。分代后,不同的代可以使用不同的算法進行垃圾收集,每種算法都可以針對性的對這個代進行優化。分代收集基于對多種編程語言的觀察得到的假設:

  • 大部分分配出來的對象存活時間不長,基本在年輕代就被回收了
  • 存在少量的老年代指向年輕代的引用

年輕代的收集執行的非常頻繁并且快速有效,因為年輕代空間通常比較小并且大都是未被引用的對象,年輕代對象在經歷指定次數的回收后依然存活會提升到老年代。老年代通常空間比較大,回收頻率比較低,但是回收一次的耗時比較長。

HotSpot將內存分成3代:年輕代,老年代,永久代。通常對象在年輕代上進行首次分配,老年代存放在年輕代上經歷了一定次數回收后依然存活的對象。有些大的對象可能直接被分配在老年代。永久代存放對象的描述信息如類和方法,永久代也受垃圾收集器管理。

年輕代分成3個區域:Eden和兩個Survivor(From和To)。通常對象在Eden上進行分配。在經歷了一次或多次年輕代的垃圾收集后依然存活的對象會被移動到Survivor的From區。

垃圾收集類型

當年輕代的被填滿時會發生Minor GC。當老年代或永久代被填充滿后會發生Full GC(有時也叫Major GC),Full GC會導致每個代都進行一次回收,通常年輕代先進行收集,然后對老年代和永久代進行收集。有時在先執行Minor GC后老年代的空間可能容納不了年輕代晉升過來的存活對象。這種情況下除CMS以外的所以收集器都會使用老年代的收集算法對整個堆進行一次回收(CMS不支持對年輕代進行回收)。

HotSpot可用的垃圾收集器

HotSpot虛擬機包含了3類垃圾收集器,分別是串行收集器,并行收集器,并發收集器。

串行收集器(Serial Collector)

串行收集器可應用于年輕代和老年代,單線程 + STW。

對于年輕代,使用復制算法,在執行串行回收時,Eden中存活的對象被復制到Survivor的To區中,有些Eden中的大對象可能不適合放到To中,會被直接放到老年代中。Survivor的From區中年輕對象也會被復制到To中,而達到老年年齡的對象會被復制到老年區。如果To區滿了,剩余在Eden和From中的所有存活對象都會被直接復制到老年區。在年輕代執行完后,Eden和From都完全變空了,只有To中包含存活對象,這時候,需要將From和To交換角色。

串行收集器在老年代和永久代上使用了標記-清除-壓縮(mark-sweep-compact)算法。在標記階段,收集器將標記所有的存活對象,在清除階段,收集器掃描整個堆,將垃圾對象進行清除回收,最后執行壓縮,將所有存活對象移動到老年代內存空間的起始位置(永久代同樣),壓縮完成后所有的空閑空間形成了一個大的連續塊。

對于大多數對停頓時間不那么敏感的程序可以選擇使用,比如客戶端(client-style)桌面或者控制臺程序,串行回收器是client應用的默認收集器,可以使用-XX:+UseSerialGC顯式啟用。

并行收集器(Parallel Collector,Throughput Collector)

在經歷了多個Java版本后,并行收集器可以說是最混亂的一個了,各個Java版本對他的命名,參數,行為定義等都可能不一樣,官方各個時期文檔對此描述有時候也會有沖突,GC Log給出的命名又和官方文檔命名不一致,而年輕代和老年代的區分也使得整體變得更加復雜。總得來說,并行收集器就是使用多線程并行進行垃圾回收的統稱,在年輕代使用復制算法,在老年代使用標記-清除-壓縮算法。了解并行收集器,最好結合JVM參數和Java版本來進行。

-XX:+UseParallelGC
  • Java 6:只指對年輕代進行并行收集,而老年代還是單線程的。
  • Java 7:和Java6一致。
  • Java 8:對年輕代進行并行收集,同時默認啟用了并行壓縮功能,也就是說老年代也是并行收集的了。
-XX:+UseParallelOldGC

Java 6,7,8版本都一樣,年輕代和老年代都是并行收集。這個收集器的官方叫法是:并行壓縮收集器(Parallel Compacting Collector),相比并行收集器,主要就是為老年代設計了新的并行收集算法,這是一個真正的全部并行的垃圾收集器。

-XX:+UseParNewGC
  • Java6:年輕代并行,老年代串行。
  • Java7:年輕代并行,老年代啟用CMS收集器(并發)。
  • Java8:廢除。

可以使用參數-XX:ParallelGCThreads=<N>來并行線程的數量,默認在Cpu核心數大于8的時候線程數量大約是5/8的Cpu核數,小于8則下降到了5/16。多線程同時進行Minor GC的時候,在年輕代對象進行到老年代的時候可能會產生內存碎片,降低線程數量可以降低碎片影響。

并發標記清除收集器(Concurrent Mark-Sweep (CMS) Collector, Low-Latency Collector)

CMS追求更短的STW時間,相比并行,引入了并發階段,使得GC線程能在并發階段和應用程序的代碼一起執行而無需停頓。CMS只能應用到老年代,所以需要配合串行或者并行收集器一起使用,默認情況下選擇CMS后,年輕代使用并行收集器。

CMS大部分時間和應用程序一起并發執行。分如下幾個步驟:

  • 初始標記:先進行一次短暫的停頓,然后識別所有可以直達(directly reachable)的存活對象。
  • 并發標記:這個階段將基于上一步的直達對象識別出所有可傳遞可達( transitively
    reachable)的存活對象。這個階段不需要停頓,而是并發執行,帶來的問題就是這個期間隨著應用程序的運行,可能有新的存活對象沒有被標記到。
  • 重新標記:遍歷所有在并發標記階段發生過變化的對象,重新標記停頓的時間比初始標記要長,這個階段使用多線程并行來提升效率。所有的存活對象在這個階段被標記。
  • 并發清除:回收所有被識別出來的垃圾對象。
不壓縮帶來的問題

CMS是不進行壓縮整理的收集器,這樣雖然節省了時間,但是由于空閑內存塊不連續,也就無法使用指針碰撞來分配內存了,而是使用了一組空閑鏈表,將所有的空閑空間鏈接起來,當需要分配內存的時候,會基于要分配的空間大小選擇一個鏈表進行搜索,以獲取一個可用空間。因此在老年代上進行內存分配的操作代價更大,同時年輕代垃圾回收也受影響,晉升到老年代花費時間變長。

浮動垃圾

由于內存緊張觸發CMS后,在并發標記階段,應用程序還可以繼續運行(不需要STW),這期間可能需要繼續申請新的內存給新對象,在并發清除階段,可能又有新的垃圾產生,這些垃圾未被進行標記,需要到下一次GC才能被清除,這樣的垃圾稱為浮動垃圾。這也導致了CMS需要使用更多的內存,通常需要準備20%的空間來給這些浮動垃圾。

并發模式失敗

CMS并不像其他收集器那樣在內存變滿的時候執行,而是試圖提前執行以便在內存滿之前執行完成,如果在內存填滿之前或者在老年代上無法分配請求的內存時,整個應用程序會被停頓,CMS會退化成并行垃圾回收,這種情況成為并發模式失敗,需要調整參數以避免這種情況。可以通過參數-XX:CMSInitiatingOccupancyFraction=<N>來控制CMS的啟動時機,N表示老年代的百分比。同時CMS采用了一些預測機制,如基于每次并發收集花費的時間和老年代填充滿需要的時間來判斷CMS的啟動時機。

增量模式

增量模式是指將CMS的并發階段的任務增量式的完成。這種模式通過定期的暫停并發任務將CPU資源還給應用程序,以減少長時間的并發對應用程序造成的影響。這項工作主要是在年輕代GC之間將任務劃分成小的時間片來完成。這個特性主要用在那些對低停頓敏感但是Cpu核數不多的情況下。啟用增量模式:–XX:+CMSIncrementalMode。

如果追求更短的停頓時間并且能將CPU資源從應用程序共享給GC(并發階段,CMS會用掉應用程序的CPU周期),可以選擇使用CMS。通常對于那些運行在雙核或者多核CPU機器上并且擁有較大老年代的應用程序來說,CMS會更加適合,比Web Server。在單核Cpu并且老年代大小適當的情況下也可以考慮使用CMS。啟用CMS:-XX:+UseConcMarkSweepGC。

G1垃圾收集器 (Garbage-First Garbage Collector)

G1目前我暫時還沒有在實際環境中使用,將使用一篇單獨的博客來記錄。

轉載于:https://www.cnblogs.com/techspace/p/6934252.html

總結

以上是生活随笔為你收集整理的Java垃圾回收总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。