介紹一些JVM內存區
Person person=new Person("張三"); 在這個語句中 哪部分是對象,哪部分是對象的引用 然后它們分別存儲在JVM的哪個區域內?
Person是類 類是抽象的 new出來之后才真正存在。所以對象另一種說法是實例,是這個類的實例。
person是Person類對象的引用
new Person("張三")是創建一個對象
person存儲在JAVA棧中 對象存儲在堆中。
一個引用可以指向多個對象,一個對象也可以指向多個引用。
如何判斷一個對象會被垃圾回收
有兩種方法,一種是引用計數法,一種是可達性分析法(JAVA中采用這種)。引用計數法就是計算一個對象是否有被引用,如果當沒有沒任何變量引用的話就可以回收了。
達性分析法:該方法的基本思想是通過一系列的“GC Roots”對象作為起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。被判定為不可達的對象要成為可回收對象必須至少經歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
舉幾個對象會被垃圾回收的例子
顯示地將某個引用賦值為null或者將已經指向某個對象的引用指向新的對象
Object obj = new Object();
obj = null;
Object obj1 = new Object();
Object obj2 = new Object();
obj1 = obj2;
obj因為指向null,所以會導致之前new的對象回收,obj1因為指向了obj2,導致沒有引用指向之前new的對象。也會被回收。
局部引用所指向的對象,比如下面這段代碼:
void fun() {
for(int i=0;i<10;i++) {
Object obj = new Object();
System.out.println(obj.getClass());
}
}
只有弱引用與其關聯的對象,比如:
WeakReference<String> wr = new WeakReference<String>(new String("world"));
有哪些典型的垃圾回收算法以及使用場景?
Mark-Sweep(標記-清除)算法、Copying(復制)算法、Mark-Compact(標記-整理)算法
介紹一下 Mark-Sweep(標記-清除)算法、Copying(復制)算法、Mark-Compact(標記-整理)算法的過程
Mark-Sweep(標記-清除)算法
標記-清除算法分為兩個階段:標記階段和清除階段。標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所占用的空間。
從圖中可以很容易看出標記-清除算法實現起來比較容易,但是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會導致后續過程中需要為大對象分配空間時無法找到足夠的空間而提前觸發新的一次垃圾收集動作。
Copying(復制)算法
為了解決產生內存碎片的問題,復制算法的特點是,每次將內存區分成兩塊相等的部分,然后使用其中一塊,當這塊內存用完之后,將存活的對象復制到另一塊,然后對已使用的那塊一次清理掉。這樣就保證了碎片不會過多。
這種算法雖然實現簡單,運行高效且不容易產生內存碎片,但是卻對內存空間的使用做出了高昂的代價,因為能夠使用的內存縮減到原來的一半。
很顯然,Copying算法的效率跟存活對象的數目多少有很大的關系,如果存活對象很多,那么Copying算法的效率將會大大降低。
Mark-Compact(標記-整理)算法
第一步標記和第一個介紹的標記清楚一樣,對使用了的內存塊進行標記。然后把存活對象都向一端移動填按順序充滿內存塊,然后清理掉端邊界以外的內存。
Generational Collection(分代收集)算法
一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),老年代的特點是每次垃圾收集時只有少量對象需要被回收,而新生代的特點是每次垃圾回收時都有大量的對象需要被回收,那么就可以根據不同代的特點采取最適合的收集算法。
新生代采用復制算法,老年代采用標記整理算法
一般來說是將新生代劃分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象復制到另一塊Survivor空間中,然后清理掉Eden和剛才使用過的Survivor空間。
注意,在堆區之外還有一個代就是永久代(Permanet Generation),它用來存儲class類、常量、方法描述等。對永久代的回收主要回收兩部分內容:廢棄常量和無用的類。
Full GC Major GC Minor GC 區別
Major GC通常是跟full GC是等價的,收集整個GC堆。也有說
Major GC是清除 老年代的
Minor GC是清除 新生代的
Full GC是清除老年代和新生代的
Minor GC的觸發條件:當新生代中的eden區分配滿的時候觸發
Full GC觸發條件:
(1)調用System.gc時,系統建議執行Full GC,但是不必然執行
(2)老年代空間不足
(3)方法區空間不足
(4)通過Minor GC后進入老年代的平均大小大于老年代的可用內存
(5)由Eden區、From Space區向To Space區復制時,對象大小大于To Space可用內存,則把該對象轉存到老年代,且老年代的可用內存小于該對象大小
有哪些典型的垃圾收集器 以及它們使用的垃圾回收算法
1.Serial/Serial Old
Serial/Serial Old收集器是最基本最古老的收集器,它是一個單線程收集器
2.ParNew
ParNew收集器是Serial收集器的多線程版本,使用多個線程進行垃圾收集。
3.Parallel Scavenge
Parallel Scavenge收集器是一個新生代的多線程收集器(并行收集器)
4.CMS
CMS(Current Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器,它是一種并發收集器,采用的是Mark-Sweep算法
對象內存分配規則
對象主要分配在新生代的Eden Space和From Space,然后如果這兩塊地區的內存不足,那么就會發生一次Minor GC ,把這兩塊的內存中的對象復制到To Space,然后將Eden Space和From Space進行清理。如果在清理的過程中,To Space無法足夠來存儲某個對象,就會將該對象移動到老年代中
在進行了GC之后,使用的便是Eden space和To Space了,下次GC時會將存活對象復制到From Space,如此反復循環。當對象在Survivor區躲過一次GC的話,其對象年齡便會加1,默認情況下,如果對象年齡達到15歲,就會移動到老年代中。
一般來說,大對象會被直接分配到老年代,所謂的大對象是指需要大量連續存儲空間的對象,最常見的一種大對象就是大數組,比如:
byte[] data = new byte[4 * 1024 * 1024]