1.java的內存
java的內存結構分爲java
- 堆 (是gc的主要區域) 線程共享,主要是用於分配實例對象和數組
- 棧 線程私有,它的生命週期和線程相同,又分紅 虛擬機棧和本地方法棧,只有它會報 StackOverFlowError,棧深度超標
- 方法區 線程共享 用於儲存被虛擬機加載的類的信息,靜態變量 常量和編譯後的.class字節碼
- 程序計數器 線程私有,線程之間不相互影響,獨立存取;
以上部分,線程私有是不會發生gc.而且他們是隨線程生隨線程滅,即程序計數器 本地方法棧和虛擬機棧
來張圖更詳細
2.GC回收機制--判斷是否能夠gc
- 引用計數算法
原理:經過一個計數器對對象進行計數,對象被引用時+1,引用失效時-1;當計數爲0時則說明能夠被回收;
缺點:很難解決對象的相互循環引用問題
- 可達性分析算法
Java虛擬機所採用的算法;
原理:經過一些列稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。
那麼哪些對象能夠被稱爲gc roots呢----虛擬機棧(棧中的本地變量列表)/方法區靜態屬性/方法區常量引用/本地方法棧中JNI 所引用的的對象都是能夠做爲 gc roots的
3.GC回收機制--如何回收
- 標記清除算法
清除算法分紅2個階段--標記和清除; 標記階段對全部存活的階段進行標記,標記完成後,再掃描整個空間未標記對象,直接回收不存活的對象.
優勢:大多數狀況下比較高效,缺點是會形成內存碎片,碎片太多致使後面過程當中對大內存的分配無足夠空間時而提早猝發一次垃圾回收動做;
- 複製算法
將可用內存將容量劃分紅大小相等的2塊,每次清理時將其中A內存還存活的對象複製到B內存裏面,而後再把A中清理掉;
優勢高效且並不產生碎片,缺點犧牲了一半的內存爲代價
適用存活對象少,回收對象多
- 標記整理算法
該算法標記階段和標記清除算法同樣,完成標記後它不是直接清理可回收對象,而是將存活對象都向一端移動最後清理掉端邊界意外的內存;
適用於存活對象多,回收對象少的狀況
- 分代收集算法
整合了複製算法和標記整理算法,根據新生代和老年代的不一樣特性採起上面的不一樣算法
新生代 生命週期短,每次回收時都有大量垃圾對象須要回收 複製算法
老年代 每次只有少許的對象須要回收 標記整理算法
深刻理解分代回收算法 Survivor(倖存者) Eden (谷歌翻譯爲伊甸園)
- 複製算法中內存劃分其實並非按照1:1來劃分老年代和新生代,,而是按照8:1:1分一個大的Eden區和兩個小的survivor的空間
- 爲何須要2個Survivor區 新生代通常經歷15次Gc就能夠移到老年代.當第一次gc時,咱們能夠把Eden的存活對象放入Survivor A空間,第二次Gc時,Survivor A也要使用複製算法,存活對象放到Survivor B上,第三次gc時,又將Survivor B對象複製到Survivor A上如此循環往復;
- 爲何Eden這麼大,由於新生代中存活的對象,須要轉移的Survivor 的對象很少,算是緩解了複製算法的缺點;
4.GC回收機制--gc的執行機制
- Scavenge GC
當新對象生成而且在Eden申請空間失敗時就會觸發Scavenge GC;Eden區的gc會比較頻繁
- Full GC
是對整個堆進行清理,要比Scavenge GC要慢,什麼狀況要進行Full GC呢,以下四種:
持久代被寫滿
System.gc調用
老年代被寫滿
上一次GC以後Heap的各域分配策略動態變化
持久代:
用於存放靜態文件,現在Java類、方法等。持久代對垃圾回收沒有顯著影響,可是有些應用可能動態生成或者調用一些class算法