JVM內存模型
根據Java虛擬機規範,Java數據區域分爲五大數據區域。javascript
其中方法區和堆是全部線程共享的,虛擬機棧、本地方法棧和程序計數器則爲線程私有的。java
有的博客稱方法區是永久代,那是由於前者是JVM的規範,然後者則是JVM規範的一種實現,而且只有HotSpot纔有永久代,web
JDK8中已經完全移除了方法區,JDK8中引入了一個新的內存區域叫metaspace(元空間),後邊詳細介紹。算法
棧區
棧分爲虛擬機棧和本地方法棧。typescript
虛擬機棧
每一個方法執行都會建立一個棧幀,用於存放局部變量表,操做棧,動態連接,方法出口等。每一個方法從被調用,直到被執行完。對應着一個棧幀在虛擬機中從入棧到出棧的過程。
一般說的棧就是指局部變量表部分,存放編譯期間可知的8種基本數據類型及對象引用和指令地址。局部變量表是在編譯期間完成分配,當進入一個方法時,這個棧中的局部變量分配內存大小是肯定的。數組
常見的的兩種異常StackOverFlowError和OutOfMemoneyError。當線程請求棧深度大於虛擬機所容許的深度就會拋出StackOverFlowError錯誤;虛擬機棧動態擴展,當擴展沒法申請到足夠的內存空間時候,拋出OutOfMemoneyError。緩存
本地方法棧
本地方法棧與虛擬機棧的做用十分相似,不過本地方法是爲native方法服務的。部分虛擬機(好比Sun HotSpot虛擬機)直接將本地方法棧與虛擬機棧合二爲一。與虛擬機棧同樣,本地方法棧也會拋出StactOverflowError與OutOfMemoryError異常。服務器
程序計數器
當前線程所執行的行號指示器。經過改變計數器的值來肯定下一條指令,好比循環,分支,跳轉,異常處理,線程恢復等都是依賴計數器來完成。微信
Java虛擬機多線程是經過線程輪流切換並分配處理器執行時間的方式實現的。爲了線程切換能恢復到正確的位置,每條線程都須要一個獨立的程序計數器,因此它是線程私有的。多線程
惟一一塊Java虛擬機沒有規定任何OutofMemoryError的區塊。
方法區
方法區/永久代是被全部線程共享區域,用於存放已被虛擬機加載的類信息、常量、靜態變量等數據。永久代的垃圾回收和老年代的垃圾回收是綁定的,一旦其中一個區域被佔滿,這兩個區都要進行垃圾回收。
在JDK1.7以前運行時常量池邏輯包含字符串常量池存放在方法區,此時hotspot虛擬機對方法區的實現爲永久代
在JDK1.7字符串常量池被從方法區拿到了堆中,這裏沒有提到運行時常量池,也就是說字符串常量池被單獨拿到堆,運行時常量池剩下的東西還在方法區,也就是hotspot中的永久代
在JDK1.8 hotspot移除了永久代用元空間(Metaspace)取而代之,這時候字符串常量池還在堆, 運行時常量池還在方法區,只不過方法區的實現從永久代變成了元空間(Metaspace)
移除永久代的影響
永久代在JDK8中被刪除,被一個叫作元空間的區域所替代了。這項改動是頗有必要的,由於對永久代進行調優是很困難的。永久代中的元數據可能會隨着每一次Full GC發生而進行移動。而且爲永久代設置空間大小也是很難肯定的,由於這其中有不少影響因素,好比類的總數,常量池的大小和方法數量等。
默認狀況下,元空間的最大可分配空間就是系統可用內存空間。所以,咱們就不會遇到永久代存在時的內存溢出錯誤,也不會出現泄漏的數據移到交換區這樣的事情。最終用戶能夠爲元空間設置一個可用空間最大值,若是不進行設置,JVM會自動根據類的元數據大小動態增長元空間的容量。
注意:永久代的移除並不表明自定義的類加載器泄露問題就解決了。還必須監控內存消耗狀況,由於一旦發生泄漏,會佔用大量的本地內存,
堆區
堆被全部線程共享區域,在虛擬機啓動時建立,惟一目的是存放對象實例。
堆區是垃圾回收的主要區域,一般狀況下分爲兩個區塊年輕代和老年代。年輕代又分爲Eden區(存放新建立對象),From survivor區和To survivor區(兩個survivor區保存gc後倖存下的對象)。默認狀況下各自佔比 8:1:1。
java虛擬機規範對這塊的描述是:全部對象實例及數組都要在堆上分配內存,但隨着逃逸分析技術的成熟,這個說法也不是那麼絕對,可是大多數狀況都是這樣的。
逃逸分析:經過逃逸分析來決定某些實例或者變量是否要在堆中進行分配,若是開啓了逃逸分析,便可將這些變量直接在棧上進行分配,而非堆上進行分配。這些變量的指針能夠被全局所引用,或者被其它線程所引用。
在JVM運行時,能夠經過配置如下參數改變整個JVM堆的配置比例
JVM運行時堆的大小
-Xms,堆的最小值
-Xmx,堆空間的最大值新生代堆空間大小調整
-XX:NewSize新生代的最小值
-XX:MaxNewSize,新生代的最大值
-XX:NewRatio,設置新生代與老年代在堆空間的大小
-XX:SurvivorRatio,新生代中Eden所佔區域的大小永久代大小調整
-XX:MaxPermSize 4.其餘
-XX:MaxTenuringThreshold,設置將新生代對象轉到老年代時須要通過多少次垃圾回收,可是仍然沒有被回收
OutOfMemoryError報錯及解決方法
java.lang.OutOfMemoryError:java heap space
這種是java堆內存不夠,一個緣由是內存真不夠,另外一個緣由是程序中有死循環。若是是java堆內存不夠的話,能夠經過調整JVM下面的配置來解決:-Xms
、-Xmx
java.lang.OutOfMemoryError:GC overhead limit exceeded
這是JDK6新增錯誤類型,當GC爲釋放很小空間佔用大量時間時拋出;通常是由於堆過小,致使異常的緣由,沒有足夠的內存。解決方案:查看系統是否有使用大內存的代碼或死循環;
經過添加JVM配置,來限制使用內存:-XX:-UseGCOverheadLimit
java.lang.OutOfMemoryError: PermGen space
這一部分用於存放Class和Meta的信息,Class在被Load的時候被放入PermGen space區域。因此若是你的APP會LOAD不少CLASS的話,就極可能出現PermGen space錯誤。這種是永久代內存不夠,可經過調整JVM的配置:-XX:MaxPermSize
、-XXermSize
java.lang.OutOfMemoryError: Direct buffer memory
可能緣由是自己資源不夠或者申請的太多內存。若是不是內存泄漏的話,可使用參數-XX:MaxDirectMemorySize
參數,或者-XX:MaxDirectMemorySize
java.lang.OutOfMemoryError: unable to create new native thread
可能緣由是系統內存耗盡,沒法爲新線程分配內存或者建立線程數超過了操做系統的限制。經過兩個途徑解決:排查應用是否建立了過多的線程。經過jstack肯定應用建立了多少線程
調整操做系統線程數閾值。操做系統會限制進程容許建立的線程數,使用ulimit -u命令查看限制。某些服務器上此閾值設置的太小,好比1024。一旦應用建立超過1024個線程,就會遇到java.lang.OutOfMemoryError: unable to create new native thread問題。若是是這種狀況,能夠調大操做系統線程數閾值。
增長機器內存。若是上述兩項未能排除問題,多是正常增加的業務確實須要更多內存來建立更多線程。若是是這種狀況,增長機器內存。
減少堆內存。一個老司機也常常忽略的很是重要的知識點:線程不在堆內存上建立,線程在堆內存以外的內存上建立。因此若是分配了堆內存以後只剩下不多的可用內存,依然可能遇到java.lang.OutOfMemoryError: unable to create new native thread。考慮以下場景:系統總內存6G,堆內存分配了5G,永久代512M。在這種狀況下,JVM佔用了5.5G內存,系統進程、其餘用戶進程和線程將共用剩下的0.5G內存,頗有可能沒有足夠的可用內存建立新的線程。若是是這種狀況,考慮減少堆內存。
減少線程棧大小。線程會佔用內存,若是每一個線程都佔用更多內存,總體上將消耗更多的內存。每一個線程默認佔用內存大小取決於JVM實現。能夠利用-Xss參數限制線程內存大小,下降總內存消耗。例如,JVM默認每一個線程佔用1M內存,應用有500個線程,那麼將消耗500M內存空間。若是實際上256K內存足夠線程正常運行,配置-Xss256k,那麼500個線程將只須要消耗125M內存。(注意,若是-Xss設置的太低,將會產生java.lang.StackOverflowError錯誤)。
java.lang.StackOverflowError
這也內存溢出錯誤的一種,即線程棧的溢出,要麼是方法調用層次過多(好比存在無限遞歸調用),要麼是線程棧過小。能夠經過優化程序設計,減小方法調用層次;調整-Xss
參數增長線程棧大小。
垃圾回收算法
新生代採用複製算法。老年代採用標記/清除算法或標記/整理算法。因爲老年代存活率高,沒有額外空間給他作擔保,必須使用這兩種算法。
標記-清除(Mark Sweep)算法
算法分爲2個階段:
標記處須要回收的對象
回收被標記的對象
標記算法分爲兩種:
引用計數算法(Reference Counting)
可達性分析算法(Reachability Analysis)
因爲引用技術算法沒法解決循環引用的問題,因此這裏使用的標記算法均爲可達性分析算法。下文將介紹兩種標記算法。
如圖所示,當進行過標記清除算法以後,出現了大量的非連續內存。當java堆須要分配一段連續的內存給一個新對象時,發現雖然內存清理出了不少的空閒,可是仍然須要繼續清理以知足「連續空間」的要求。因此說,這種方法比較基礎,效率也比較低下。
複製(Copying)算法
爲了解決效率與內存碎片問題,複製(Copying)算法出現了,它將內存劃分爲兩塊相等的大小,每次使用一塊,當這一塊用完了,就將還存活的對象複製到另一塊內存區域中,而後將當前內存空間一次性清理掉。這樣的對整個半區進行回收,分配時按照順序從內存頂端依次分配,這種實現簡單,運行高效。不過這種算法將原有的內存空間減小爲實際的一半,代價比較高。
從圖中能夠看出,整理後的內存十分規整,可是白白浪費通常的內存成本過高。然而這實際上是很重要的一個收集算法,由於如今的商業虛擬機都採用這種算法來回收新生代。IBM公司的專門研究代表,新生代中的對象98%都是「朝生夕死」的,因此不須要按照1:1的比例來劃份內存。HotSpot虛擬機將Java堆劃分爲年輕代(Young Generation)、老年代(Tenured Generation),其中年輕代又分爲一塊Eden和兩塊Survivor。
全部的新建對象都放在年輕代中,年輕代使用的GC算法就是複製算法。其中Eden與Survivor的內存大小比例爲8:2,其中Eden由1大塊組成,Survivor由2小塊組成。每次使用內存爲1Eden+1Survivor,即90%的內存。因爲年輕代中的對象生命週期每每很短,因此當須要進行GC的時候就將當前90%中存活的對象複製到另一塊Survivor中,原來的Eden與Survivor將被清空。可是這就有一個問題,咱們沒法保證每次年輕代GC後存活的對象都不高於10%。因此在當活下來的對象高於10%的時候,這部分對象將由Tenured進行擔保,即沒法複製到Survivor中的對象將移動到老年代。
標記-整理算法
複製算法在極端狀況下(存活對象較多)效率變得很低,而且須要有額外的空間進行分配擔保。因此在老年代中這種狀況通常是不適合的。
因此就出現了標記-整理(Mark-Compact)算法。與標記清除算法同樣,首先是標記對象,然而第二步是將存貨的對象向內存一段移動,整理出一塊較大的連續內存空間。
垃圾回收的幾種形式
Minor GC
在年輕代(包括Eden區和Survivor區)中的垃圾回收稱之爲 Minor GC。Minor GC當年輕代中eden區分配滿的時候觸發,只會清理年輕代。通過此次GC後,Eden區和From區已經被清空。這個時候,「From」和「To」會交換他們的角色,也就是新的「To」就是上次GC前的「From」,新的「From」就是上次GC前的「To」。無論怎樣,都會保證名爲To的Survivor區域是空的。
Full GC
full gc是收集整個堆,包括young gen、old gen、perm gen(若是存在的話)、元空間(1.8及以上)等全部部分的模式。
手動調用System.gc()方法 [增長了full GC頻率,不建議使用而是讓jvm本身管理內存,能夠設置-XX:+ DisableExplicitGC來禁止RMI調用System.gc]
發現perm gen(若是存在永久代的話)需分配空間但已經沒有足夠空間
老年代空間不足,好比說新生代的大對象大數組晉升到老年代就可能致使老年代空間不足。
mixed GC(G1特有)
混合GC 收集整個young gen以及部分old gen的GC。只有G1有這個模式
垃圾回收的兩種斷定方法
1. 引用計數算法
在JDK1.2以前,使用的是引用計數器算法,即當這個類被加載到內存以後,就會產生方法區,堆棧、程序計數器等一系列信息,當建立對象的時候,爲這個對象在堆棧空間中分配對象,同時會產生一個引用計數器,同時引用計數器+1,當有新的引用時,引用計數器繼續+1,而當其中一個引用銷燬時,引用計數器-1,當引用計數器減爲0的時候,標誌着這個對象已經沒有引用了,能夠回收了!可是這樣會有一個問題:當咱們的代碼出現這樣的狀況時:
ObjA.obj=ObjB
ObjB.obj=ObjA
這樣的代碼會產生以下引用情形ObjA指向ObjB,而ObjB又指向objA,這樣當其餘全部的引用都消失了以後,ObjA和ObjB還有一個相互的引用,也就是說兩個對象的引用計數器各爲1,而實際上這兩個對象都已經沒有額外的引用,已是垃圾了。
2.可達性分析算法
可達性分析算法是從離散數學中的圖論引入的,程序把全部的引用關係看作一張圖,從一個節點GC Root開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個節點的引用節點,當全部的引用節點尋找完畢以後,剩餘的節點則被認爲是沒有被引用到的節點,即無用的節點。
目前Java中可做爲GC Root的對象有:
虛擬機棧中引用的對象(本地變量表)
方法區中靜態屬性引用的對象
方法區中常量引用的對象(final的常量值)
本地方法棧中引用的對象(Native對象)。
java中存在的四種引用
強引用:只要引用存在,垃圾回收器永遠不會回收。
軟引用:非必須引用,內存溢出以前進行回收。代碼示例:
Object obj=new Object();SoftReference<Object> sf=new SoftRerence<Object>(obj);obj=null;sf.get();//有時會返回null
這時候sf是對obj的一個軟引用,經過sf.get()方法能夠取到這個對象,固然這個對象被標記爲須要回收的對象時,則返回null;
軟引用主要用於用戶實現相似緩存的功能,在內存不足的狀況下直接經過軟引用取值,無需從繁忙的真實來源查詢數據,提高速度;當內存不足時,自動刪除這部分緩存數據,從真實的來源查詢這些數據。
弱引用:弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,不管內存是否充足,都會回收被弱引用關聯的對象。能夠經過以下代碼實現
Object obj=new Object();WeakReference<Object> wf=new WeakReference<Object>(obj);obj=null;wf.get();//有時會返回nullwf.isEnQueued();//返回是否被垃圾回收器標記爲即將回收的垃圾
弱引用是在第二次垃圾回收時回收,短期內經過弱引用取對應的數據,能夠取到,當執行過第二次垃圾回收時,將返回null。弱引用主要用於監控對象是否已經被標記爲即將回收的垃圾,能夠經過弱引用的isEnQueues方法返回對象是否被垃圾回收器標記。
虛引用:虛引用和前面的軟引用、弱引用不一樣,它並不影響對象的生命週期。在java中用java.lang.ref.PhantomReference類表示。若是一個對象與虛引用關聯,則跟沒有引用與之關聯同樣,在任什麼時候候均可能被垃圾回收器回收。
要注意的是,虛引用必須和引用隊列關聯使用,當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會把這個虛引用加入到與之 關聯的引用隊列中。程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。能夠經過以下代碼實現
import java.lang.ref.ReferenceQueue; public class Main { public static void main(String[] args) { ReferenceQueue<String> queue = new ReferenceQueue<String>(); PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue); System.out.println(pr.get()); } }
垃圾收集器
若是說垃圾回收算法是內存回收的方法論,那麼垃圾收集器就是具體實現。jvm會結合針對不一樣的場景及用戶的配置使用不一樣的收集器。
年輕代收集器:
Serial、ParNew、Parallel Scavenge
老年代收集器:
Serial Old、Parallel Old、CMS收集器
特殊收集器:
G1收集器(新型,不在年輕、老年代範疇內)
年輕代收集器
Serial
最基本、發展最久的收集器,在jdk3之前是gc收集器的惟一選擇,Serial是單線程收集器,Serial收集器只能使用一條線程進行收集工做,在收集的時候必須得停掉其它線程,等待收集工做完成其它線程才能夠繼續工做。
雖然Serial看起來很坑,需停掉別的線程以完成本身的gc工做,可是也不是徹底沒用的,好比說Serial在運行在Client模式下優於其它收集器(簡單高效,不過通常都是用Server模式,64bit的jvm甚至沒Client模式)
串行收集器組合 Serial + Serial Old
優勢:對於Client模式下的jvm來講是個好的選擇。適用於單核CPU(如今基本都是多核了) 缺點:收集時要暫停其它線程,有點浪費資源,多核下顯得。
ParNew收集器
能夠認爲是Serial的升級版,由於它支持多線程[GC線程],並且收集算法、Stop The World、回收策略和Serial同樣,就是能夠有多個GC線程併發運行,它是HotSpot第一個真正意義實現併發的收集器。默認開啓線程數和當前cpu數量相同,若是cpu核數不少不想用那麼多,能夠經過-XX:ParallelGCThreads來控制垃圾收集線程的數量。
優勢:
支持多線程,多核CPU下能夠充分的利用CPU資源
運行在Server模式下新生代首選的收集器【重點是由於新生代的這幾個收集器只有它和Serial能夠配合CMS收集器一塊兒使用】
缺點: 在單核下表現不會比Serial好,因爲在單核能利用多核的優點,在線程收集過程當中可能會出現頻繁上下文切換,致使額外的開銷。
Parallel Scavenge
採用複製算法的收集器,和ParNew同樣支持多線程。 可是該收集器重點關心的是吞吐量(吞吐量 = 代碼運行時間 / (代碼運行時間 + 垃圾收集時間) 若是代碼運行100min垃圾收集1min,則爲99%) 對於用戶界面,適合使用GC停頓時間短,否則由於卡頓致使交互界面卡頓將很影響用戶體驗。 對於後臺高吞吐量能夠高效率的利用cpu儘快完成程序運算任務,適合後臺運算
並行收集器組合 Parallel Scavenge + Parallel Old
Parallel Scavenge注重吞吐量,因此也成爲"吞吐量優先"收集器。
JDK7和8中,做爲年輕代默認的收集器
老年代收集器
Serial Old
和新生代的Serial同樣爲單線程,Serial的老年代版本,不過它採用"標記-整理算法",這個模式主要是給Client模式下的JVM使用。 若是是Server模式有兩大用途:
jdk5前和Parallel Scavenge搭配使用,jdk5前也只有這個老年代收集器能夠和它搭配。
做爲CMS收集器的後備。
Parallel Old
支持多線程,Parallel Scavenge的老年版本,jdk6開始出現,採用"標記-整理算法"。 在jdk6之前,新生代的Parallel Scavenge只能和Serial Old配合使用,並且Serial Old爲單線程Server模式下沒法充分利用多核cpu,這種結合並不能讓應用的吞吐量最大化。
Parallel Old的出現結合Parallel Scavenge,真正的造成「吞吐量優先」的收集器組合。 JDK7和8中,做爲老年代默認的收集器
CMS收集器
CMS收集器(Concurrent Mark Sweep)是以一種獲取最短回收停頓時間爲目標的收集器。(重視響應,能夠帶來好的用戶體驗,被sun稱爲併發低停頓收集器)啓用CMS:-XX:+UseConcMarkSweepGC
正如其名,CMS採用的是"標記-清除"(Mark Sweep)算法,並且是支持併發的。
它的運做分爲4個階段:
初始標記(initial mark):標記一下GC Roots能直接關聯到的對象
併發標記(concurrent mark):併發標記就須要標記出GC roots 關聯到的對象的引用對象有哪些。好比說 A -> B (A引用B,假設A是GC Roots關聯到的對象),那麼這個階段就是標記出B對象,A對象會在初始標記中標記出來。這個過程是能夠和用戶線程併發執行的。所謂的併發的實現,能夠有幾種方式,好比說,標記了100個對象,那麼就停一停,讓用戶線程跑一會;再好比說,標記了10ms,再停一停,之類的實現。
從新標記(remark):爲了修正因併發標記期間用戶程序運做而產生變更的那一部分對象的標記記錄,會有些許停頓,時間上通常 初始標記 < 從新標記 < 併發標記
併發清除(sweep):將前面標記對象的內存回收,這個階段GC線程與用戶線程併發運行。
以上初始標記和從新標記須要停掉其它運行java線程。之因此說CMS的用戶體驗好,是由於CMS收集器的內存回收工做是能夠和用戶線程一塊兒併發執行。 整體上CMS是款優秀的收集器,可是它也有缺點:
cms對cpu特別敏感,cms運行線程和應用程序併發執行須要多核cpu,若是cpu核數多的話能夠發揮它併發執行的優點,可是cms默認配置啓動的時候垃圾線程數爲 (cpu數量+3)/4,它的性能很容易受cpu核數影響,當cpu的數目少的時候好比說爲爲2核,若是這個時候cpu運算壓力比較大,還要分一半給cms運做,這可能會很大程度的影響到計算機性能。
cms沒法處理浮動垃圾,可能致使Concurrent Mode Failure(併發模式故障)而觸發full GC
因爲cms是採用"標記-清除「算法,所以就會存在垃圾碎片的問題,爲了解決這個問題cms提供了-XX:+UseCMSCompactAtFullCollection選項,這個選項至關於一個開關(默認開啓),用於CMS要進行full GC時開啓內存碎片合併,內存整理的過程是沒法併發的,且開啓這個選項會影響性能(好比停頓時間變長)
Concurrent mode failure:若是CMS回收過程尚未執行完,老年代的剩餘空間就用完了,或者,當前老年代空間不能知足一次內存分配請求(可能對象較大),那麼此時將觸發擔保機制,停頓全部用戶線程,串行老年代收集器將會以STW的方式進行一次GC,從而形成較大停頓時間;
浮動垃圾:因爲cms支持運行的時候用戶線程也在運行,程序運行的時候會產生新的垃圾,這裏產生的垃圾就是浮動垃圾,cms沒法當次處理,得等下次才能夠。
假若有一個對象GC線程沒有標記(用戶線程以前沒在用),而後輪到了用戶線程,用戶線程說,這個對象我從新又要用了,不要把這個對象GC掉,這個時候怎麼辦?假如這個時候處理不了,仍是GC了,那麼程序就直接報錯了,這個是不容許的,解決辦法能夠百度搜索「cms 三色標記法」獲取答案
G1收集器
G1(garbage first)收集器是當前最爲前沿的收集器之一(1.7之後纔開始有),同cms同樣也是關注下降延遲,是用於替代cms功能更爲強大的新型收集器,由於它解決了cms產生空間碎片等一系列缺陷。
當G1肯定有必要進行垃圾回收時,它會先收集存活數據最少的區域(垃圾優先) g1的特別之處在於它強化了分區,弱化了分代的概念,是區域化、增量式的收集器,它不屬於新生代也不屬於老年代收集器。用到的算法爲標記-清理、複製算法
G1是區域化的,它將java堆內存劃分爲若干個大小相同的區域"region「,JVM能夠設置每一個region的大小(1-32m,大小得看堆內存大小,必須是2的冪),它會根據當前的堆內存分配合理的region大小。
g1經過併發(並行)標記階段查找老年代存活對象,經過並行複製壓縮存活對象(這樣能夠省出連續空間供大對象使用)。g1將一組或多組區域中存活對象以增量並行的方式複製到不一樣區域進行壓縮,從而減小堆碎片,目標是儘量多回收堆空間,且儘量不超出暫停目標以達到低延遲的目的。g1提供三種垃圾回收模式 young gc、mixed gc 和 full gc,不像其它的收集器,根據區域而不是分代,新生代老年代的對象它都能回收。幾個重要的默認值,更多的查看官方文檔oracle官方g1中文文檔 g1是自適應的回收器,提供了若干個默認值,無需修改就可高效運做
-XX:G1HeapRegionSize=n 設置g1 region大小,不設置的話本身會根據堆大小算,目標是根據最小堆內存劃分2048個區域
-XX:MaxGCPauseMillis=200 最大停頓時間 默認200毫秒
JDK9中,G1做爲默認的收集器
JDK7/8,默認關閉的,開啓選項 -XX:+UseG1GC
更詳細的G1垃圾回收介紹,請查看這篇文章: https://blog.csdn.net/coderlius/article/details/79272773
喜歡就點個「在看」唄^_^
本文分享自微信公衆號 - 全菜工程師小輝(mseddl)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。