1.強引用( StrongReference )併發
當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足的問題(不管什麼時候都不會回收)jvm
public static void main(String[] args) { List list = new ArrayList<>(); int i = 1; while (i++ < Integer.MAX_VALUE) { list.add("ssssss"); } System.out.println("-------end-------"); }
2.軟引用 (SoftReference)高併發
若是一個對象只具備軟引用,則內存空間足夠,垃圾回收器就不會回收它;若是內存空間不足了,就會回收這些對象的內存(內存不足時候回收)spa
當內存足夠的時候不會被回收,因此能正常輸出對象的地址線程
public static void main(String[] args) { // 定義一個軟引用 SoftReference<byte[]> soft = new SoftReference<byte[]>(new byte[1024 * 1024 * 1024]); // 消耗內存 for(int i = 0; i < 10;i++) { byte[] buff = new byte[1024 * 1024]; } // 建議執行GC System.gc(); // 打印結果 System.out.println(soft.get()); }
下面咱們把內存使用再 擴大1024倍, 當內存不夠的時候,GC回收了內存,因此輸出的內存地址就爲null設計
public static void main(String[] args) { // 定義一個軟引用 SoftReference<byte[]> soft = new SoftReference<byte[]>(new byte[1024 * 1024 * 1024]); // 消耗內存 for(int i = 0; i < 10;i++) { byte[] buff = new byte[1024 * 1024 * 1024]; } // 建議執行GC System.gc(); // 打印結果 System.out.println(soft.get()); }
3.弱引用(WeakReference)對象
只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它的內存區域,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存(不管內存足不足都回收)blog
下面就是一個弱引用的例子,這個是軟引用內存足夠狀況下的那個例子直接改爲弱引用而來,證實:證實了即便是內存足夠的狀況下,只要執行了GC,就必定會回收弱引用生命週期
public static void main(String[] args) { // 定義一個弱引用 WeakReference<byte[]> weak = new WeakReference<byte[]>(new byte[1024 * 1024 * 1024]); // 消耗內存 for(int i = 0; i < 10;i++) { byte[] buff = new byte[1024 * 1024]; } // 建議執行GC System.gc(); // 打印結果 System.out.println(weak.get()); }
這裏面也能夠看出一個問題,就是執行到一半的時候,GC忽然回收了咱們的弱引用類型,致使沒法正常完成,可是因爲GC的線程優先級比通常線程的優先級都要低,保證在咱們使用完對象以後再回收,使咱們的系統更加穩定隊列
4.虛引用(PhantomReference)
虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收,不過虛引用的做用不是爲了咱們使用對象,而是爲了記錄。
爲一個對象設置虛引用關聯的惟一目的就是能在這個對象被收集器回收時收到一個系統通知
由於虛引用的對象咱們是使用不到的,它的get()始終返回一個null
public T get() {
return null;
}
建立虛引用的時候須要傳入1個引用對象,和一個引用隊列,當該虛引用對象被回收以後,不會立刻銷燬,而會將這個虛引用加入引用隊列
public PhantomReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); }
咱們再看看下面的例子
public static void main(String[] args) { // 定義一個引用隊列 ReferenceQueue<byte[]> queue = new ReferenceQueue<byte[]>(); // 定義一個軟引用類型 PhantomReference<byte[]> phantom = new PhantomReference<byte[]>(new byte[1024 * 1024],queue); // 第一次打印,這個時候還沒執行內存回收,因此隊列裏面是null System.out.println(queue.poll()); // 消耗內存 for(int i = 0; i < 10;i++) { byte[] buff2 = new byte[1024 * 1024 * 1024]; } // 建議執行GC System.gc(); // 若是對象被回收,則會放進隊列 System.out.println(queue.poll()); }
注意:以上全部例子的System.gc(); 並非執行GC的語句,而是建議jvm執行GC,並非執行了System.gc(); 就100%執行內存回收
高併發狀況下會把咱們內存撐爆,由於咱們的對象引用由棧內存指向堆內存且咱們常常使用的是強引用,若是棧內存同時存在太多強引用的時候,棧內存沒來得及釋放,強引用過多一直存在致使GC沒法回收內存,這個時候就會使咱們系統出現OOM。若是設計好線程數量使棧內不會有過多引用,合理釋放掉棧內存引用,這個時候堆內存沒有引用的狀況下,則會回收掉,避免了高併發致使的OOM