Java 垃圾回收

在這里插入圖片描述
一、概述

GC

GC(Garbage Collection),在程序運行過程中內存空間是有限的,為了更好的的使用有限的內存空間,GC會將不再使用的對象清除然后將其所占用的內存釋放出來。

java的垃圾回收機制

Java的垃圾收集(Garbage Collection, GC)是Java虛擬機(JVM)提供的一種自動管理機制,用于回收和再分配無任何引用的對象所占用的內存
Java的垃圾收集機制是Java語言一個顯著的特點,它可以有效防止內存泄漏,保證內存的有效復用。
Java的垃圾收集機制最初由Sun在JDK 1.0中提出,經過多次更新和優化,現在的JVM(如HotSpot)采用了多種算法,如標記-清除(Mark-Sweep)、(Copying)等。

各版本默認GC

# cmd命令行查看Java8的GC
java -XX:+PrintCommandLineFlags -version# 輸出:
-XX:InitialHeapSize=132397312 // JVM默認初始化堆大小
-XX:MaxHeapSize=2118356992 //JVM堆的默認最大值
-XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC //Java8使用的GC類型
java version "1.8.0_20" //使用的java版本
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)

結果分析:由結果可以看出Java8的GC情況是:-XX:+UseParallelGC,即Parallel Scavenge(新生代) + Parallel Old(老生代),實際上幾個主流Java版本的GC情況如下:

  • jdk1.7
    默認垃圾收集器Parallel Scavenge(新生代【標記-復制算法】)+Parallel Old(老年代【標記整理算法】)
  • jdk1.8
    默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
  • jdk1.9 及以上
    默認垃圾收集器G1【從局部(兩個Region之間)來看是基于"標記—復制"算法實現,從整體來看是基于"標記-整理"算法實現】

GC詳細情況

# cmd命令行查看Java8的GC詳細情況
java -XX:+PrintGCDetails -version# 輸出:
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
HeapPSYoungGen      total 38400K, used 2678K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)eden space 33280K, 8% used [0x00000000d5e00000,0x00000000d609dbc0,0x00000000d7e80000)from space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)to   space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)ParOldGen       total 87552K, used 0K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)object space 87552K, 0% used [0x0000000081a00000,0x0000000081a00000,0x0000000086f80000)Metaspace       used 2257K, capacity 4480K, committed 4480K, reserved 1056768Kclass space    used 244K, capacity 384K, committed 384K, reserved 1048576K

二、對象回收判定方式

當一個對象被創建時,虛擬機會優先分配到堆空間中,當對象不再被使用了,虛擬機會對其進行回收處理,以便釋放內存空間,這個過程也被稱為垃圾對象回收。

那么如何找到對象是否可以進行回收呢?一般有兩種方式。

  • 引用計數法

  • 可達性分析法

下面我們一起來了解下相關知識。

2.1、引用計數法

這個方法的實現思路是:在對象中維護一個引用計數器,每當一個地方引用這個對象時,計數器值+1;當引用失效時,計數器值-1。當對象的計數器值為 0,表示這個對象不再被使用了,可以被回收。

這種方法使用場景很多,但很少有垃圾收集器會使用這種方式。

原因在于:這種方式存在一個致命的缺陷,比如堆中的兩個對象相互引用,此時他們的計數器值是 1,但這兩個對象并沒有被外部使用,因此不會被回收,容易造成內存泄露。

2.2、可達性分析法

這個方法的實現思路是:從“GC Roots”(這個 GC Roots 可以是棧中的引用變量,也可以是方法區的引用變量或常量)開始掃描堆中的對象,沿著 GC Roots 一路掃描,被掃描的所有對象全部標記為存活對象;掃描完成之后,沒有被標記的視為垃圾對象,可以被回收。

比如對象 A 被線程占中的變量 a 引用著,對象 A 中引用著對象 B,對象 B 又引用著 C 等,沿著 a 開始掃描,會掃描到對象 A,B,C 等,并把它們標記為存活對象。全部掃描完成之后,當一個對象到 GC Roots 沒有任何引用鏈時,表示此對象是不可用的,等待被 GC 回收。
在這里插入圖片描述

在 JVM 中,可以作為 GC Roots 的對象包括:

  • 虛擬機棧中引用的對象

  • 方法區中靜態屬性引用的對象

  • 方法區中常量引用的對象

  • 本地方法棧中 JNI(即 Native 方法)引用的對象

三、垃圾回收算法

當一個對象被判定為垃圾對象之后,剩下的工作就是如何進行回收了。

下面我們一起來看看常見的幾種垃圾回收算法的思想。

3.1、標記-清除算法

標記-清除算法如同它的名字一樣,分為“標記”和“清除”兩個階段,也是最基礎的算法。

首先標記出所有需要回收的對象,標記完成后統一回收所有被標記的對象。之所以說它是最基礎的收集算法,是因為后續的收集算法都是基于這種思路并對其缺點進行改進而得到的。

這個算法也有很多的不足,主要體現在效率和空間。

  • 從效率的角度講,標記和清除兩個過程的效率都不高;

  • 從空間的角度講,標記清除后會產生大量不連續的內存碎片,空間碎片太多可能會導致后面的程序運行過程中分配較大對象時,無法找到足夠的連續內存而不得不提前觸發一次垃圾收集動作

標記-清除算法執行過程,可以用如下圖來概括:

在這里插入圖片描述

3.2、復制算法

復制算法是為了解決效率問題而出現的,它將可用的內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。

這樣每次只需要對整個半區進行內存回收,內存分配時也不需要考慮內存碎片等復雜情況,只需要移動指針,按照順序分配即可。

這個算法也有缺點,操作的時候內存會縮小為了原來的一半,代價很高;其次,持續復制長生存期的對象會導致回收效果不佳,效率較低。

一般的商用虛擬機會采用這種算法來回收新生代(也稱為年輕代)的對象,不過研究表明1:1的比例不是很科學,因此新生代的內存空間被細劃分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 和其中一塊 Survivor;每次回收時,將 Eden 和 Survivor 空間中還存活的對象一次性復制到另外一塊 Survivor 空間上,最后清理掉之前的 Eden 和 Survivor 空間。

HotSpot 虛擬機默認 Eden 和 Survivor 區的比例是8 : 1 : 1,期望每次回收后只有不到 10% 的對象存活,如果出現 Survivor 空間不夠用時,需要依賴老年代進行分配擔保。

復制算法執行過程,可以用如下圖來概括:

在這里插入圖片描述

3.3、標記-壓縮算法

在上面我們提到了復制算法的優點和缺點,針對對象存活率較高的場景,進行大量的復制操作時,效率很低下。如果不想浪費 50% 的空間,當對象 100% 存活時,那么需要有額外的空間進行分配擔保。

在 HotSpot 虛擬機中,堆空間劃分成兩個不同的區域:新生代老年代,目的是為了更有效率的回收對象。新生代的對象存活率低,會優先被回收,如果多次執行依然沒有被回收,就會轉移到老年代。老年代都是不易被回收的對象,對象存活率高,因此一般不能直接選用復制算法。

根據老年代的特點,有人提出了另外一種標記-整理算法,也稱為標記-壓縮算法,過程與標記-清除算法一樣,不過不是直接對可回收對象進行清理,而是讓所有存活對象都向一端移動,然后直接清理掉邊界外的內存。

標記-整理算法執行過程,可以用如下圖來概括:

在這里插入圖片描述

3.4、分代收集算法

分代收集算法,可以看成以上內容的延伸。它的實現思路是根據對象的生命周期的不同,將內存劃分為幾塊,比如把堆空間劃分為新生代老年代,然后根據各塊的特點采用最適當的收集算法。

在新生代中,存在大批對象死去、少量對象存活的特點,會采用復制算法,只需要付出少量存活對象的復制成本就可以完成垃圾對象收集,效率高;在老年代中,存在對象存活率高、沒有額外空間對它進行分配擔保的特點,會采用**“標記-清理”或者“標記-整理”**算法來進行回收。

可以用如下圖來概括堆內存的空間布局:

在這里插入圖片描述

四、垃圾收集器

如果說收集算法是內存回收的方法論,那么垃圾收集器就是內存回收的具體實現。

不同的虛擬機所提供的垃圾收集器可能會有很大差異,以 HotSpot 虛擬機為例,所包含的垃圾收集器可以用如下圖來概括。
在這里插入圖片描述

上圖中的連線表示,不同分代的收集器可以搭配使用。

  • 新生代收集器:Serial、ParNew、Parallel Scavenge

  • 老年代收集器:Serial Old、CMS、Parallel Old

  • 通用收集器: G1

在虛擬機中,沒有所謂的萬能收集器,只有根據具體的業務場景,選擇最合適的收集器。這也是為什么 HotSpot 實現了這么多收集器的原因。

下面我們一起來看看相關的具體實現。

4.1、Serial 和 Serial Old收集器

Serial 系列的垃圾收集器是 JVM 的第一款收集器,它的設計思路很簡單,在新生代,使用單線程采用復制算法進行收集對象;在老年代,使用單線程采用標記整理算法進行收集對象;垃圾收集的過程中會暫停用戶線程,直到垃圾收集完畢。

因為當時的硬件環境配置都不高,內存都是幾十兆,CPU 也都是單核的,不像現在這樣處處都是高并發的應用場景。限于當時的硬件資源和應用場景,這個收集器優勢很突出,簡單高效、消耗資源也很少。

唯一的不足在于,在用戶不可見的情況下要把用戶正常工作的線程全部停掉,這對很多應用比較難以接受。不過實際上到目前為止,Serial 收集器依然是虛擬機在 Client 模式下運行的默認新生代收集器,因為它簡單而高效。客戶端應用模型下,分配給虛擬機管理的內存一般來說不會很大,收集幾十兆甚至一兩百兆的新生代對象,停頓時間平均在幾十毫秒,只要不是頻繁收集,完全可以接受。

整個流程,可以用如下圖來概括。
在這里插入圖片描述

總結下來,收集器特點如下:

  • 收集區域: Serial(新生代),Serial Old(老年代)

  • 收集算法: Serial(復制算法),Serial Old(標記整理算法)

  • 收集方式:單線程

  • 優勢:簡單高效,內存資源占用少,單核 CPU 環境最佳選項

  • 劣勢:整個搜集過程需要停頓用戶線程,多核 CPU、大內存的環境,資源優勢無法發揮起來

4.2、ParNew收集器

ParNew 收集器,可以看成是 Serial 收集器的多線程版本。除了使用多線程進行垃圾收集外,其余行為和 Serial 收集器完全一樣,包括使用的也是復制算法,垃圾收集時暫停用戶線程。在多核 CPU 資源環境下,可以顯著提升整個垃圾收集的性能,也是虛擬機在 Server 模式下運行的首選新生代收集器。

能讓 ParNew 出名的一個核心因素是,它是除了 Serial 收集器外,目前唯一一個能與 CMS 收集器配合一起使用的新生代收集器,因為 CMS 優秀所以 ParNew 也出名了,有點類似碰上了大款的感覺,其中 CMS 收集器是一款幾乎可以認為有劃時代意義的垃圾收集器,下文我們再講。

其次,ParNew 收集器在單個 CPU 的環境中絕對不會有比 Serial 收集器更好的效果,甚至由于線程交互的開銷,該收集器在兩個 CPU 的環境中都不能百分之百保證可以超越 Serial 收集器。當然,隨著可用 CPU 數量的增加,它對于垃圾收集的效率提升還是很有幫助的。

整個流程,可以用如下圖來概括。

在這里插入圖片描述

總結下來,收集器特點如下:

  • 收集區域:新生代

  • 收集算法:復制算法

  • 收集方式:多線程

  • 優勢:多線程收集,多核 CPU 環境下效率要比 serial 高,新生代中,除了 Serial 收集器外目前唯一一個能與 CMS 配合的收集器

  • 劣勢:整個搜集過程需要停頓用戶線程

4.3、Parallel Scavenge 和 Parallel Old收集器

Parallel Scavenge 和 ParNew 收集器很類似,也是一款使用多線程采用復制算法的新生代收集器;Parallel Old 收集器是一款使用多線程采用標記整理算法的老年代收集器;垃圾收集過程中也會暫停用戶線程,直到整個垃圾收集過程結束。

不同的是,Parallel 收集器更關注系統的吞吐量,也被稱為“吞吐量優先收集器”。

所謂吞吐量的意思就是 CPU 用于運行用戶代碼時間與 CPU 總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),比如虛擬機總運行 100 分鐘,垃圾收集 1 分鐘,那吞吐量就是 99%。高吞吐量可以高效率的利用 CPU 資源,盡快完成程序的運算任務,主要適合在后臺運算而不需要太多交互的任務。

自適應調節策略也是 Parallel Scavenge 與 ParNew 的一個重要區別,用戶可以通過參數來打開自適應調節策略,比如-XX:+UseAdaptiveSizePolicy參數,打開之后就不需要手動指定新生代大小、Eden 區和 Survivor 參數等細節參數了,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量。如果對于垃圾收集器運作原理不太了解,優化比較困難的情況下,使用 Parallel 收集器配合自適應調節策略,把內存管理的調優任務交給虛擬機去完成也是一個不錯的選擇。

另外,Parallel 收集器是虛擬機在 Server 模式下運行的默認垃圾收集器。

整個執行流程,跟 ParNew 收集器類似。

總結下來,收集器特點如下:

  • 收集區域:Parallel Scavenge(新生代),Parallel Old(老年代)

  • 收集算法:Parallel Scavenge(復制算法),Parallel Old(標記整理算法)

  • 收集方式:多線程

  • 優勢:多線程收集,多核 CPU 環境下效率要比 serial 高

  • 劣勢:整個搜集過程需要停頓用戶線程

4.4、CMS收集器

CMS 收集器是一種以獲取最短回收停頓時間為目標的老年代收集器

與前面幾個收集器不同,它采用了一種全新的策略可以在垃圾回收過程中的某些階段用戶線程和垃圾回收線程一起工作,從而避免了因為長時間的垃圾回收而使用戶線程一直處于等待之中。

目前很大一部分 Java 應用集中在互聯網站或者 B/S 系統的服務端上,這類應用尤其注重服務的響應速度,希望系統停頓時間最短,比如在一個長度為 M 毫秒的時間片段內,消耗在垃圾收集上的時間不得超過多少毫秒,以期給用戶帶來較好的體驗,其中 CMS 收集器就非常符合這類應用的需求。

CMS 的英文全程是:Concurrent Mark-Sweep Collector,從名字上就能看出 CMS 收集器是基于“標記-清除”算法實現的,它的運作過程相對于前面幾種收集器來說要更復雜一些,整個過程分為如下 4 個步驟:

  • 初始標記

  • 并發標記

  • 重新標記

  • 并發清除

CMS 會根據每個階段不同的特性來決定是否停頓用戶線程。整個流程,可以用如下圖來概括。(圖片來自于勤勞的小手 - 垃圾收集器文章)

在這里插入圖片描述

4.4.1、階段一:初始標記

初始標記階段的工作主要是標記一下 GC Roots 能直接關聯到的對象,這個過程會短暫的停頓用戶線程,因為并不會對整個 GC Roots 的引用進行遍歷,因此速度很快。

4.4.2、階段二:并發標記

并發標記階段的工作主要是把階段一標記好的 GC Roots 對象進行深度的遍歷,找到所有與 GC Roots 關聯的對象并進行標記,這個過程會采用多線程的方式進行遍歷標記,因為非常耗時,CMS 考慮到為了盡量不停頓用戶線程,因此這個階段不會暫停用戶線程,也就是說,此時 JVM 會分配一些資源給用戶線程執行任務,通過這樣的方式減少用戶線程的停頓時間。

4.4.3、階段三:重新標記

重新標記階段的工作主要是修補階段二用戶線程運行期間產生新的垃圾對象,進行重新標記,同樣也是采用多線程方式進行,此階段數量不會很多,會短暫的停頓用戶線程,速度也很快。

4.4.4、階段四:并發清除

并發清除階段的工作主要是對那些被標記為可回收的對象進行清理,在一般情況下,并發清除階段是使用的是“標記-清除”算法,因為這個過程不會牽扯到對象的地址變更,所以 CMS 在并發清除階段是不需要停止用戶線程的,對象回收效率非常高。

與此同時,正因為并發清除階段用戶線程也可以同時運行,所以在用戶線程運行的過程中自然也會產生新的垃圾對象,這也就是導致 CMS 收集器會產生“浮動垃圾”的原因,此時也會產生很多的空間碎片,當空間碎片到達了一定程度時,此時 CMS 就會使用“標記-整理”算法來解決空間碎片的問題。

在上文的垃圾回收算法中我們有說到,“標記-整理”算法會將對象的位置進行挪動并更新對象的引用的指向地址,在這個過程中,如果用戶線程同時運行的話會產生并發問題,因此當 CMS 進行碎片整理的時候必須得停止用戶線程。所以,在某些情況下,并發清除階段 CMS 也會停頓用戶線程。

CMS 收集器作為一個全新思路的垃圾收集器,雖然很優秀,但一直沒有被 Hospot 虛擬機納入為默認的垃圾收集器。時至今日,JDK1.8 使用的默認收集器都還是 Parallel scavenge 和 Parallel old 收集器,主要原因在于 CMS 存在一些比較頭疼的問題,比如浮動垃圾、空間碎片整理時會造成系統卡頓、在并發清除階段可能會出現系統長時間的假死。

4.4.5、小結

總結下來,收集器特點如下:

  • 收集區域:老年代

  • 收集算法:標記清除算法 + 標記整理算法

  • 收集方式:多線程

  • 優勢:多線程收集過程中可以做到不停止用戶線程,以獲取最短回收停頓時間

  • 劣勢:會產生浮動垃圾、空間碎片整理時會造成系統卡頓、并發清除階段可能會出現系統假死等問題

4.5、G1收集器

G1(Garbage-First)收集器是當今收集器技術發展的最前沿成果之一,從 JDK 7 Update 4 后開始進入商用。

在 G1 收集器出現之前,不管是 Serial 系列,Parallel 系列,還是 CMS 收集器,它們都是基于把內存進行物理分區的形式將 JVM 內存分成新生代、老年代、永久代或 MetaSpace,這種分區模式下進行垃圾收集時必須對某個區域進行整體性的收集,比如整個新生代、整個老年代收集或者整個堆,當內存空間不大的時候,比如幾個 G,通過參數優化能取得不錯的收集性能。但是,隨著硬件資源的發展,JVM 可用內存從幾十 G 到幾百 G 甚至上 T 時,這種采用傳統模式下的物理分區進行收集時,每次掃描內存的區域自然就變大了,進行垃圾清理的時間自然就變得更長了,此時傳統的收集器即時再怎么優化,也難以取得令人滿意的收集效果,因此需要一款全新的垃圾收集器。

G1 收集器就是在這樣的環境下誕生的,它摒棄了原來的物理分區,把整個 Java 堆分成若干個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離,它們都是一部分 Region 的集合。從結構上看,G1 收集器不要求整個新生代或者老年代都是連續的,也不再堅持固定大小和固定數量,它會跟蹤各個 Region 里面的垃圾堆積的價值大小,在后臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的 Region。這種通過 Region 劃分內存空間以及有優先級的區域回收方式,保證了 G1 收集器在有限的時間內可以獲取盡可能高的收集效率。

G1 收集器內存劃分,可以用如下圖來概括。(圖片來自于勤勞的小手 - 垃圾收集器文章)
在這里插入圖片描述

在 G1 收集器里面維護了一個 Collect Set 集合,這個集合里面記錄了待回收的 Region 區域信息,同時也包括了每個 Region 區域可回收的大小空間。通過 Collect Set 里面的信息,G1 在進行垃圾收集時,可以根據用戶設定的可接受停頓時間來進行分析,在設定的時間范圍內優先收集垃圾最多的 Region 區域,以實現高吞吐、低停頓的收集效果。

在工作流程上,G1 收集器也吸收了 CMS 很多優秀的收集思路,整個垃圾收集過程,可以分為如下 4 個步驟:

  • 初始標記

  • 并發標記

  • 重新標記

  • 篩選回收

G1 收集器的垃圾回收流程和 CMS 邏輯大致相同,主要的區別在最后一個階段,G1 不會直接進行清除,而是會根據設置的停頓時間進行智能的篩選和局部的回收,采用“標記復制”算法來實現。

整個流程,可以用如下圖來概括。
在這里插入圖片描述

4.5.1、階段一:初始標記

此階段的工作內容與上文介紹的 CMS 收集器一樣,會先把所有 GC Roots 直接引用的對象進行標記,同時會短暫的停止用戶線程,因為不會對整個 GC Roots 的引用進行遍歷,因此速度比較快。

4.5.2、階段二:并發標記

此階段的工作內容與上文介紹的 CMS 收集器也一樣,找到所有與 GC Roots 關聯的對象并進行深度遍歷標記,會采用多線程的方式進行遍歷標記,因為比較耗時,為了盡量不停頓用戶線程,這個階段 GC 線程會和用戶線程同時運行,通過這樣的方式減少用戶線程的停頓時間。

4.5.3、階段三:重新標記

此階段的工作內容與上文介紹的 CMS 收集器也是一樣,針對階段二用戶線程運行的過程中產生新的垃圾,采用多線程方式進行重新標記,為了避免這個過程再次產生新的垃圾對象,會短暫的停止用戶線程,因為數量不會很多,因此速度比較快。

4.5.4、階段四:篩選回收

篩選回收階段的工作主要是把存活的對象復制到 Region 空閑區域,同時會根據 Collect Set 記錄的可回收 Region 信息進行篩選,計算 Region 回收成本,接著根據用戶設定的停頓時間值制定回收計劃,最后根據回收計劃篩選合適的 Region 區域進行垃圾回收。

從局部來看,G1 使用的是復制算法,將存活對象從一個 Region 區域復制到另一個 Region 空閑區域;但從整個堆來看,G1 使用的邏輯又相當于標記整理算法,每次垃圾收集時會把存活的對象整理到對應可用的 Region 區域,再把原來的 Region 區域標記為可回收區域并記錄到 Collect Set 中,因此 G1 的每一次回收都可以看作是一次標記整理過程,兩者都不會產生空間碎片問題。

4.5.5、小結

總結下來,收集器特點如下:

  • 收集區域:整個堆內存

  • 收集算法:復制算法

  • 收集方式:多線程

  • 優勢:停頓時間可控,吞吐量高,不會產生空間碎片,不需要額外的收集器搭配

  • 劣勢:目前而言,相較于 CMS,G1 還不具備全方位、壓倒性優勢,G1 在收集過程中內存占用和執行負載都偏高;其次,在小內存應用上 CMS 的表現大概率會優于 G1,而 G1 在大內存應用上會比較有優勢,6G 以上的內存可以考慮使用 G1 收集器

4.6、常用的收集器組合

最后我們對以上介紹的垃圾收集器進行一次匯總,同時介紹一下服務器端常用的組合模式,內容如下。

服務器組合新生代收集器老年代收集器備注
組合一SerialSerial OldSerial 是一個使用單線程采用復制算法的新生代收集器;Serial Old 是一個使用單線程采用標記整理算法的老年代收集器,GC 時會暫停所有應用線程,可以使用-XX:+UseSerialGC選項來開啟
組合二ParNewSerial OldParNew 是一個使用多線程采用復制算法的新生代收集器,GC 時會暫停所有應用線程,可以使用-XX:+UseParNewGC選項來開啟
組合三Parallel ScavengeSerial OldParallel Scavenge 是一個使用多線程采用復制算法的新生代收集器,GC 時會暫停所有應用線程,可以使用-XX:+UseParallelGC選項來開啟;需要注意的是,在jdk1.7及之前的版本中,這個參數默認采用 Serial Old 作為老年代收集器;在jdk1.8及之后的版本中,默認采用 Parallel Old 作為老年代收集器
組合四Parallel ScavengeParallel OldParallel Old是 Serial Old 的多線程版收集器,可以使用-XX:+UseParallelOldGC選項來開啟
組合五SerialCMS + Serial OldCMS 是一個使用多線程采用標記清楚算法的老年代收集器,可以實現 GC 線程和用戶線程并發工作,不需要暫停所有用戶線程;另外,可以將 Serial Old 收集器作為備選,當 CMS 進行 GC 失敗時,會自動使用 Serial Old 進行 GC;可以使用-XX:+UseConcMarkSweepGC選項來開啟
組合六ParNewCMS + Serial OldParNew 是除了 Serial 以外,唯一一個能搭配 CMS 的新生代收集器;可以使用-XX:+UseConcMarkSweepGC開啟,默認使用 ParNew 作為新生代收集器,也可以通過-XX:+UseParNewGC強制指定 ParNew
組合七G1G1G1 是一個新一代的垃圾收集器,摒棄了原來的物理分區,把整個 Java 堆分成若干個大小相等的獨立區域(Region),針對局部區域使用多線程采用復制算法進行篩選回收,可以使用-XX:+UseG1GC選項來開啟

五、方法區回收

以上介紹的都是對象的回收過程,在之前的 JVM 內存結構的文章中我們介紹到,Java 應用程序運行時,除了堆空間會存在垃圾數據以外,方法區同樣也存在。

雖然虛擬機規范中沒有明確要求方法區一定要實現垃圾回收,主要原因在于這個區域的垃圾回收效率非常低,但是 HotSpot 虛擬機對方法區也會進行回收的,主要回收的是廢棄常量無用的類兩部分。

如何判斷一個常量是否為“廢棄常量”呢?其實很簡單,只要當前系統中沒有任何一處引用該常量,就會被判定為廢棄常量。

如何判斷一個類是否為“無用的類”呢?條件非常苛刻,需要同時滿足以下三點。

  • 1.該類所有實例都已經被回收,也就是說 Java 堆中不存在該類的任何實例

  • 2.該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法

  • 3.加載該類的 ClassLoader 已經被回收,也就是說這個類的類加載器被卸載回收了

滿足以上三個條件則表示這個類再也無用了,HotSpot 虛擬機會對此類進行回收。例如在大量使用反射、動態代理、CGLib 等 ByteCode 框架,并自定義 ClassLoader 創建的類,為了保證方法區不會溢出,虛擬機會在適當的情況下對無用的類進行回收。

在 JDK1.7 及以前的版本中,用永久代來作為方法區的實現,當永久代的空間不足時會觸發 Full GC。

在 JDK1.8 及之后的版本中,用元空間來作為方法區的實現,元空間的內存空間默認使用的是操作系統的內存空間,它的垃圾回收不再由 Java 來控制,元空間的內存管理由元空間虛擬機來完成。

好文分享,一起加油!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19946.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19946.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19946.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

嵌入式Linux復制剪切刪除指令詳解

指令操作 1. cp 復制指令 a. 用法:cp [ 選項 ] [ 源文件或目錄 ] [ 目標文件或目錄 ]; b. 用途:用于復制文件或目錄; c. 通常情況下,復制的都不是空文件夾,所以直接使用 cp 復制空文件會失敗&#xff0…

創建Django項目及應用

1 創建Project 1個Project可以對應多個app django-admin startproject myproject 2 創建App python manage.py startapp app01 INSTALLED_APPS [# ...app01,app02,# ... ] 如果要讓這個應用在項目中起作用,需要在項目的 settings.py 文件的 INSTALLED_APPS 配置…

java中成員內部類、局部內部類、匿名內部類各自的特點

成員內部類:定義在類的內部,方法的外部,成員內部類作為外部類的成員,可以直接訪問外部類的私有屬性。 局部內部類:定義在方法的內部,對于局部內部類我們常常使用一個方法,得到一個接口實現類的…

臭氧濃度傳感器在食品廠與制藥廠中的應用

在食品廠和制藥廠的生產過程中,消毒是一個至關重要的環節。有效的消毒可以確保產品免受微生物污染,從而保障消費者的健康。近年來,臭氧作為一種廣譜殺菌劑,因其強效的消毒能力和低污染性,在食品廠和制藥廠的消毒過程中…

SpringMVC:創建一個簡單的SpringMVC框架

目錄 一、框架介紹 兩個重要的xml文件 SpringMVC執行流程 二、Vscode搭建SpringMVC框架 1、maven創建webapp原型項目 2、pom.xml下添加springmvc的相關依賴 3、在web.xml配置 4、springmvc.xml的配置 5、編寫Controller控制器類 6、 編寫JSP界面 7、項目結構圖 一…

VS2017中使用qt翻譯家,除ui界面外其他用tr包裹的字符串在翻譯家中顯示為亂碼

1、ui界面中的中文,可以正常顯示 2、其他用tr包裹的字符串,顯示為亂碼 3、解決 改為utf8保存。 然后更新翻譯文件,重新打開發現已經ok了。 參考博客: https://blog.csdn.net/zhou714534957/article/details/124948822 https://blog.csdn.net/weixin_52689816/article/d…

【Linux】期末復習

《Linux程序設計》各章知識點梳理 第1章 軟件包的管理方式方面,Ubuntu、CentOS的差異 Ubantu使用APT,CentOS使用YUM 如何添加一個新用戶? Useradd new_user_name 什么是Shell? Shell 是一個用 C 語言編寫的程序,這個…

Milvus向量數據庫:高效處理海量非結構化數據的利器

一、引言 隨著數據量的爆炸式增長,如何高效地存儲、管理和查詢海量非結構化數據成為數據科學和人工智能領域的一個重大挑戰。傳統的關系型數據庫在處理這種類型的數據時顯得力不從心,而向量數據庫作為一種新型的數據庫解決方案,提供了極大的…

PAT-1004 成績排名(java實現)

這一關感覺還沒第三關難,思路很清晰 題目 1004 成績排名 讀入 n(>0)名學生的姓名、學號、成績,分別輸出成績最高和成績最低學生的姓名和學號。 輸入格式: 每個測試輸入包含 1 個測試用例,格式為 第 1 行…

【算法】宵暗的妖怪

?題目鏈接: 宵暗的妖怪 ?題目描述 露米婭作為宵暗的妖怪,非常喜歡吞噬黑暗。這天,她來到了一條路上,準備吞噬這條路上的黑暗。這條道路一共被分為n 部分,每個部分上的黑暗數量為ai 。露米婭每次可以任取 連續的 未被…

賺錢其實沒有秘密,多琢磨一下不丟人

為什么學了很多知識還是掙不到錢? 掙不到錢,是因為你不夠稀缺;掙錢太少,是因為你不懂杠桿,用杠桿撬動稀缺,個人價值自然水漲船高。 學富五車,為何財庫依舊空空?怎樣才能提高掙錢的…

在全志H616核桃派開發板上配置SSH遠程終端方法詳解

熟悉指令用戶可以對已經聯網的核桃派進行局域網SSH遠程終端控制,方便使用自己的PC對核桃派遠程進行各種指令操作。 普通用戶(默認) 賬號:pi ; 密碼:pi管理員賬戶 賬號:root ; 密碼:root 在這之…

在Android Studio中使用谷歌Gemini代碼助手

今天在做android開發的時候,一個項目使用到了gradle8.0,但是我的Android Studuio根本不支持,無可奈何只能從小蜜蜂版本升級了水母 | 2023.3.1版本,但突然發現AS已經集成了Gemini助手。 首先我們需要下載這個版本的: h…

2.5Bump Mapping 凹凸映射

一、Bump Mapping 介紹 我們想要在屏幕上繪制物體的細節,從尺度上講,一個物體的細節分為:宏觀、中觀、微觀宏觀尺度中其特征會覆蓋多個像素,中觀尺度只覆蓋幾個像素,微觀尺度的特征就會小于一個像素宏觀尺度是由頂點或…

JDBC常見異常(10)—預編譯模式下占位符動態排序字段失效

場景需求 需要根據不同的列進行對應的排序操作,實現動態列名排序 類似🐟動態查詢或更新 但是JDBC預編譯模式下占位符的排序字段失效 SQL語句 分頁查詢 select * from (select t.*, rownum rn from(select * from emp order by empno desc) t where …

《java數據結構》--一篇解決二叉搜索樹!!

😸二叉搜索樹的概念 二叉搜索樹又名二叉排序樹,一般具有以下性質: 若它的左子樹不為空,則左子樹上所有節點的值都小于根節點的值若它的右子樹不為空,則右子樹上所有節點的值都大于根節點的值它的左右子樹也分別為二叉…

C語言高級編程及實例剖析.pdf

C語言高級編程及實例剖析.pdf C語言,作為一種經典且強大的編程語言,已經在多個領域得到廣泛應用。然而,要想真正掌握C語言的高級編程技巧,卻并非易事。本文將深入探討C語言的高級編程技巧,并結合實例進行詳細剖析&…

61. UE5 RPG 實現敵人近戰攻擊技能和轉向攻擊

在前面,我們實現了敵人的AI系統,敵人可以根據自身的職業進行匹配對應的攻擊方式。比如近戰戰士會靠近目標后進行攻擊然后躲避目標的攻擊接著進行攻擊。我們實現了敵人的AI行為,但是現在還沒有實現需要釋放的技能,接下來&#xff0…

HTML5 音頻 Audio 標簽詳解

HTML5 引入了 <audio> 標簽&#xff0c;允許開發者在網頁中直接嵌入音頻文件&#xff0c;而不需要依賴第三方插件。本文將全面介紹 <audio> 標簽的各種屬性&#xff0c;并通過實例代碼詳細說明其用法。 一、基礎用法 1. 基本結構 HTML5 中使用 <audio> 標…

通過定時器和脈沖控制LED

目錄 一、定時器 &#xff08;一&#xff09;定時器簡介 &#xff08;二&#xff09;定時器類型 1、常見定時器 2、定時器的主要功能 3、常規定時器 &#xff08;三&#xff09;定時器配置 1、定時器標準外設庫接口函數 2、定時器標準外設庫配置 二、PWM &#xff08…