JAVA的垃圾回收機制

Java引入了垃圾回收機制,令C++程序員最頭疼的內存管理問題迎刃而解。JAVA程序員可以將更多的精力放到業務邏輯上而不是內存管理工作上,大大的提高了開發效率。

垃圾回收原理和算法

1)內存管理

Java的內存管理很大程度指的就是對象的管理,其中包括對象空間的分配和釋放。

對象空間的分配:使用new關鍵字創建對象即可

對象空間的釋放:將對象賦值null即可。垃圾回收器將負責回收所有不可達對象的內存空間。

 

2)垃圾回收過程

任何一種垃圾回收算法一般要做兩件基本事情:

1. 發現無用的對象

2. 回收無用對象佔用的內存空間。 

垃圾回收機制保證可以將“無用的對象”進行回收。無用的對象指的就是沒有任何變量引用該對象。 JAVA的垃圾回收器通過相關算法發現無用對象,並進行清除和整理。


3)垃圾回收相關算法

a. 引用計數法

堆中每個對象都有一個引用計數。被引用一次,計數加1. 被引用變量值變爲null,則計數減1. 直到計數爲0,則表示變成無用對象。 優點是算法簡單,缺點是循環引用的無用對象無法別識別。

【示例1】 循環引用示例

public class Student   {

    String name;

    Student friend;

   

    public static void   main(String[] args) {

        Student s1 = new   Student();

        Student s2 = new   Student();

       

        s1.friend =   s2;

        s2.friend =   s1;

       

        s1 = null;

        s2 = null;

    }

}

s1s2互相引用了對方,導致他們的引用計數不爲0,實際已經無用,但是無法被識別。

 

b. 引用可達法(根搜索算法)

程序把所有的引用關係看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點則被認爲是沒有被引用到的節點,即無用的節點。

通用的分代垃圾回收機制

分代垃圾回收機制,是基於這樣一個事實:不同的對象的生命週期是不一樣的。因此,不同生命週期的對象可以採取不同的回收算法,以便提高回收效率。我們將對象分爲三種狀態:年輕代、年老代、持久代。JVM將堆內存劃分爲 EdenSurvivor  Tenured/Old 空間。

1)年輕代

所有新生成的對象首先都是放在Eden區。 年輕代的目標就是儘可能快速的收集掉那些生命週期短的對象,對應的是Minor GC每次 Minor GC 會清理年輕代的內存,算法採用效率較高的複製算法,頻繁的操作,但是會浪費內存空間。當年輕代區域存放滿對象後,就將對象存放到年老代區域。

2)年老代

在年輕代中經歷了N(默認15)次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認爲年老代中存放的都是一些生命週期較長的對象。年老代對象越來越多,我們就需要啓動MajorGCFull GC(全量回收),來一次大掃除,全面清理年輕代區域和年老代區域。

3)持久代

用於存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響。

Minor GC:

用於清理年輕代區域。Eden區滿了就會觸發一次Minor GC。清理無用對象,將有用對象複製到“Survivor1”“Survivor2”區中。

Major GC

用於清理老年代區域。

Full GC

用於清理年輕代、年老代區域。 成本較高,會對系統性能產生影響。

JVM調優和Full GC

在對JVM調優的過程中,很大一部分工作就是對於FullGC的調節。有如下原因可能導致Full GC

1) 年老代(Tenured)被寫滿

2) 持久代(Perm)被寫滿

3) System.gc()被顯示調用

4) 上一次GC之後Heap的各域分配策略動態變化

開發中容易造成內存泄露的操作

建議:

   在實際開發中,經常會造成系統的崩潰。如下這些操作我們應該注意這些使用場景。 

如下四種情況時最容易造成內存泄露的場景,請大家開發時一定注意:

1)創建大量無用對象

比如,我們再需要大量拼接字符串時,使用了String而不是StringBUilder

String   str = "";

       for (int i   = 0; i < 10000; i++) {  

           str += i;     //相當於產生了10000String對象

       }

2)靜態集合類的使用

HashMapVectorList等的使用最容易出現內存泄露,這些靜態變量的生命週期和應用程序一致,所有的對象Object也不能被釋放。

3)各種連接對象(IO流對象、數據庫連接對象、網絡連接對象)未關閉

4)監聽器的使用

釋放對象時,沒有刪除相應的監聽器

 

要點

1.程序員無權調用垃圾回收器。

2.程序員可以通過System.gc()。只是通知JVM並不是運行垃圾回收器。儘量少用,會申請啓動Full GC,成本高,影響系統性能。

3.finalize方法,是Java提供給程序員用來釋放對象或資源的方法,但是儘量少用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章