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; } } |
s1和s2互相引用了對方,導致他們的引用計數不爲0,實際已經無用,但是無法被識別。
b. 引用可達法(根搜索算法)
程序把所有的引用關係看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點則被認爲是沒有被引用到的節點,即無用的節點。
通用的分代垃圾回收機制
分代垃圾回收機制,是基於這樣一個事實:不同的對象的生命週期是不一樣的。因此,不同生命週期的對象可以採取不同的回收算法,以便提高回收效率。我們將對象分爲三種狀態:年輕代、年老代、持久代。JVM將堆內存劃分爲 Eden、Survivor 和 Tenured/Old 空間。
1)年輕代
所有新生成的對象首先都是放在Eden區。 年輕代的目標就是儘可能快速的收集掉那些生命週期短的對象,對應的是Minor GC,每次 Minor GC 會清理年輕代的內存,算法採用效率較高的複製算法,頻繁的操作,但是會浪費內存空間。當“年輕代”區域存放滿對象後,就將對象存放到年老代區域。
2)年老代
在年輕代中經歷了N(默認15)次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認爲年老代中存放的都是一些生命週期較長的對象。年老代對象越來越多,我們就需要啓動MajorGC和Full 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; //相當於產生了10000個String對象 } |
2)靜態集合類的使用
像HashMap、Vector、List等的使用最容易出現內存泄露,這些靜態變量的生命週期和應用程序一致,所有的對象Object也不能被釋放。
3)各種連接對象(IO流對象、數據庫連接對象、網絡連接對象)未關閉
4)監聽器的使用
釋放對象時,沒有刪除相應的監聽器
要點
1.程序員無權調用垃圾回收器。
2.程序員可以通過System.gc()。只是通知JVM並不是運行垃圾回收器。儘量少用,會申請啓動Full GC,成本高,影響系統性能。
3.finalize方法,是Java提供給程序員用來釋放對象或資源的方法,但是儘量少用