以棧或寄存器中的引用【GC Roots】爲起點,找到堆中的對象,又從這些對象找到對堆中其餘對象的引用,這種引用逐步擴展,最終以null引用或者基本類型結束,這樣就造成了一顆以Java棧中引用所對應的對象爲根節點的一顆對象樹,若是棧中有多個引用,則最終會造成多顆對象樹。
在這些對象樹上的對象,都是當前系統運行所須要的對象,不能被垃圾回收。而其餘剩餘對象,則能夠視爲沒法被引用到的對象,能夠被當作垃圾進行回收。所以,垃圾回收的起點是一些根對象(java棧, 靜態變量, 寄存器…)。而最簡單的Java棧就是Java程序執行的main函數。這便是「標記-清除」的回收方式。java
GC Roots包括:算法
- 虛擬機棧中引用的對象。
- 方法區中類靜態屬性實體引用的對象。
- 方法區中常量引用的對象。
- 本地方法棧中JNI引用的對象。
通常狀況下,當新對象生成且在Eden申請空間失敗時,就會觸發Minor GC。
對Eden區域進行GC,清除非存活對象,而且把尚且存活的對象移動到Survivor區;而後整理Survivor的兩個區。由於大部分對象都是從Eden區開始的,同時Eden區不會分配的很大,因此Eden區的GC會頻繁進行。因此通常在這裏須要使用速度快、效率高的算法 - 複製,使Eden去能儘快空閒出來。數組
虛擬機給每一個對象定義了一個對象年齡(Age)計數器,能夠經過參數 -XX:MaxTenuringThreshold (默認爲 15 歲)來設置。
若是對象在 Eden 出生並通過第一次 Scavenge GC 後仍然存活,而且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將對象年齡設爲 1。對象在 Survivor 區中每熬過一次 Scavenge GC,年齡就增長 1 歲,當它的年齡增長到必定程度時,就會晉升到老年代中。對象晉升老年代的年齡閾值。服務器
對整個堆進行整理,包括Young、Tenured和MetaSpace。由於須要對整個對進行回收,因此很慢,所以應該儘量減小Full GC的次數。在對JVM調優的過程當中,很大一部分工做就是對於FullGC的調節。多線程
有以下緣由可能致使Full GC:併發
Major GC一般是跟full GC是等價的,收集整個GC堆。但由於HotSpot VM發展了這麼多年,外界對各類名詞的解讀已經徹底混亂了,當說「major GC」的時候必定要問清楚他想要指的是上面的full GC仍是old gen。app
Parallel Scavenge(-XX:+UseParallelGC)框架下:
默認是在要觸發full GC前先執行一次young GC,而且兩次GC之間能讓應用程序稍微運行一小下,以期下降full GC的暫停時間(由於young GC會盡可能清理了young gen的死對象,減小了full GC的工做量)。
以CMS GC (-XX:+UseConcMarkSweepGC)爲例,它主要是定時去檢查old gen的使用量,當使用量超過了觸發比例(-XX:CMSInitiatingOccupancyFraction)就會啓動一次CMS GC,對old gen作併發收集。經過配置-XX:+CMSScavengeBeforeRemark開啓或關閉在CMS從新標記階段以前的清除(YGC)嘗試。框架
所謂"大對象" 就是指一個佔用大量連續內存空間的對象。(如很長的字符串及數組)
若在Eden區 或 當前使用Survior區中存不下, 就須要把Eden區 或 當前使用Survior區的存活對象都移動到老年代中去,而後再將新對象存入Eden區。
一個大對象可以存入Eden區及 當前使用Survior區的機率比較小, 發生分配擔保的機率比較大, 而分配擔保須要涉及到大量的複製,就會形成效率低下。
所以: 大對象直接把放到老年代中去,從而就能避免大量的複製操做!
-XX:PretenureSizeThreshold 參數 該參數用於設置大小超過該參數的對象被認爲是"大對象", 直接分配在老年代。(注意: 該參數只對Serial和ParNew收集器有效。)ide
爲了使內存分配更加靈活,年齡相同對象的內存大小總和超過了任一Survivor空間的一半, 那麼全部年齡相同及超過的對象都會被轉移到老年代中,無須等到MaxTenuringThreshold要求的年齡。函數
分配擔保是老年代爲新生代做擔保,擔保有足夠的空間存入新生代可存活的對象, 若是OldGeneration空間還不夠就OOM。
發生MinorGC前, JVM首先會檢查老年代中最大可用的連續空間是否大於新生代中全部對象的大小。若此條件:
在jdk5後,晉升不須要連續空間了。
In 5.0 we added the ability in the low pause collector to start a young generation collection and then to back out of it if there was not enough space in the tenured generation. Being able to backout of a young generation collection allowed us to make a couple of changes. We now keep an average of the amount of space that is used for promotions and use that (with some appropriate padding to be on the safe side) as the requirement for the space needed in the tenured generation. Additionally we no longer need a single contiguous chunk of space for the promotions so we look at the total amount of free space in the tenured generation in deciding if we can do a young generation collection. Not having to have a single contiguous chunk of space to support promotions is where fragmentation comes in (or rather where it doesn't come in as often).
此外,咱們再也不須要一個連續的空間大塊晉升,因此咱們經過老年代的自由空間量總數決定是否能夠作一個新生代的回收。沒必要擁有一個連續的空間來支持晉升活動是兼容碎片化的狀況https://blog.csdn.net/fei33423/article/details/70941113?utm_source=blogxgwz4
根據JVM的內存結構對堆進行分代收集:
在新生代中,每次垃圾收集都是有大批對象死去,少許存活; 且分Eden區+2個Survivor區。 比較適合複製算法。
而老年代中,對象存活率高,沒有額外空間供其分配,就必須用「標記-清理-整理」算法。 常見如: CMS(Concurrent Mark-Sweep)以犧牲吞吐量爲代價來得到最短回收停頓時間。對於要求服務器響應速度的應用上,這種垃圾回收器很是適合。在啓動JVM參數加上-XX:+UseConcMarkSweepGC 表示對於老年代的回收採用CMS。其基礎算法是:標記—清除。因此須要增長-XX:+UseCMSCompactAtFullCollection、-XX:CMSFullGCsBeforeCompaction來開啓「整理」操做。
串行(Serial)收集: 使用單線程處理全部垃圾回收工做;
並行(ParNew)收集: 並行收集使用多線程處理垃圾回收工做,於是速度快,效率高。並且理論上CPU數目越多,越能體現出並行收集器的優點。
併發(Parallel)收集: 能夠保證大部分工做都併發進行(應用不中止),垃圾回收只暫停不多的時間 。相對於串行收集和並行收集而言,前面兩個在進行垃圾回收工做時,須要暫停整個運行環境,而只有垃圾回收程序在運行。所以,系統在垃圾回收時會有明顯的暫停,並且暫停時間會由於堆越大而越長。(ps: 因此堆內存大小分配須要合理分片,並非越大越好)