接上一篇文章
11、棧又稱棧記憶體,主管程式的執行,生命週期和執行緒同步,執行緒結束,棧記憶體就釋放了,不存在垃圾回收
棧:先進後出佇列:先進先出(FIFO)1、棧中存放啥?8大基本型別物件引用例項的方法2、棧執行原理棧表示Java方法執行的記憶體模型每呼叫一個方法就會為每個方法生成一個棧幀(Stack Frame),每個方法被呼叫和完成的過程,都對應一個棧幀從虛擬機器棧上入棧和出棧的過程。程式正在執行的方法一定在棧的頂部3、堆疊溢位StackOverflowError舉個例子:
public class Test { public static void main(String[] args) { new Test().a(); } public void a() { b(); } public void b() { a(); }}
最開始,main()方法壓入棧中,然後執行a(),a()壓入棧中;再呼叫b(),b()壓入棧中;以此往復,a與b方法不斷被壓入棧中,最終導致棧溢位
12、堆Heap,一個JVM只有一個堆記憶體(棧是執行緒級的),堆記憶體的大小是可以調節的
1、堆中有啥?例項化的物件
2、堆記憶體詳解1、Young 年輕代物件誕生、成長甚至死亡的區
Eden Space(伊甸園區):所有的物件都是在此new出來的Survivor Space(倖存區)倖存0區(From Space)(動態的,From和To會互相交換)倖存1區(To Space)Eden區佔大容量,Survivor兩個區佔小容量,預設比例是8:1:1。
2、Tenured 老年代3、Perm 元空間儲存的是Java執行時的一些環境或類資訊,這個區域不存在垃圾回收!關閉虛擬機器就會釋放這個區域記憶體!
這個區域常駐記憶體,用來存放JDK自身攜帶的Class物件、Interface元資料。
名稱演變
jdk1.6之前:永久代jdk1.7:永久代慢慢退化,去永久代jdk1.8之後:永久代改名為元空間注意:元空間在邏輯上存在,在物理上不存在
新生代 + 老年代的記憶體空間 = JVM分配的總記憶體
如圖所示:
3、什麼是OOM?記憶體溢位java.lang.OutOfMemoryError產生原因:
分配的太少用的太多用完沒釋放4、GC垃圾回收GC垃圾回收,主要在年輕代和老年代
首先,物件出生在伊甸園區
假設伊甸園區只能存一定數量的物件,則每當存滿時就會觸發一次輕GC(Minor GC)經GC清理後,有的物件可能還存在引用,就活下來了,活下來的物件就進入倖存區;有的物件沒用了,就被GC清理掉了;每次輕GC都會使得伊甸園區為空如果倖存區和伊甸園都滿了,則會進入老年代,如果老年代滿了,就會觸發一次重GC(FullGC),年輕代+老年代的物件都會清理一次,活下的物件就進入老年代如果新生代和老年代都滿了,則OOMMinor GC:伊甸園區滿時觸發;從年輕代回收記憶體
Full GC:老年代滿時觸發;清理整個堆空間,包含年輕代和老年代
Major GC:清理老年代
什麼情況永久區會崩?
一個啟動類載入了大量的第三方Jar包,Tomcat部署了過多應用,或者大量動態生成的反射類
這些東西不斷的被載入,直到記憶體滿,就會出現OOM
13、堆記憶體調優1、檢視並設定JVM堆記憶體檢視我們jvm的堆記憶體
public class Test { public static void main(String[] args) { //返回jvm試圖使用的最大記憶體 long max = Runtime.getRuntime().maxMemory(); //返回jvm的初始化記憶體 long total = Runtime.getRuntime().totalMemory(); //預設情況下:分配的總記憶體為電腦記憶體的1/4,初始化記憶體為電腦記憶體的1/64 System.out.println("max=" + max / (double) 1024 / 1024 / 1024 + "G"); System.out.println("total=" + total / (double) 1024 / 1024 / 1024 + "G"); }}
預設情況下:
JVM最大分配記憶體為電腦記憶體的1/4JVM初始化記憶體為電腦記憶體的1/64我們可以手動調堆記憶體大小
在VM options中可以指定jvm試圖使用的最大記憶體和jvm初始化記憶體大小
-Xms1024m -Xmx1024m -Xlog:gc*
-Xmx用來設定jvm試圖使用的最大記憶體,預設為1/4-Xms用來設定jvm初始化記憶體,預設為1/64-Xlog:gc*用來列印GC垃圾回收資訊2、怎麼排除OOM錯誤?1. 嘗試擴大堆記憶體看結果
利用上述方法指定jvm試圖使用的最大記憶體和jvm初始化記憶體大小
2. 利用記憶體快照工具JProfiler記憶體快照工具:
MAT(Eclipse)JProfiler作用:
分析Dump記憶體檔案,快速定位記憶體洩漏獲得堆中的檔案獲得大的物件…3. 什麼是Dump檔案?如何分析?Dump檔案是程序的記憶體映象,可以把程式的執行狀態透過偵錯程式儲存到dump檔案中
import java.util.ArrayList;public class Test { byte[] array = new byte[1024 * 1024];//1M public static void main(String[] args) { ArrayList<Test> list = new ArrayList<>(); int count = 0; try { while (true) { list.add(new Test()); count++; } } catch (Exception e) { System.out.println("count=" + count); e.printStackTrace(); } }}
執行該程式,報錯OOM
接下來我們設定一下堆記憶體,並附加生成對應的dump檔案的指令
-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError表示當JVM發生OOM時,自動生成DUMP檔案。
我們雙擊開啟,可以看到每塊所佔的大小,便於分析問題
附:安裝Jprofiler教程1.idea中安裝外掛
2.下載客戶端 https://www.ej-technologies.com/download/jprofiler/files
3.安裝客戶端
選擇自定義安裝,注意:路徑不能有中文和空格
這裡name和Company任意,License Key大家可以尋找對應版本的註冊機獲得
後續預設,安裝成功即可!!!
4. 安裝完成後,重啟IDEA,可以看到我們的記憶體快照工具
開啟IDEA的設定,找到Tools裡面的JProfiler,沒有設定位置則設定位置
此時則全部安裝完成!
14、GC垃圾回收1、回顧Garbage Collection:垃圾回收
在12.4中,我們已經對GC的流程進行了大概的講解,這裡做一些總結:
JVM在進行GC時,並不是對年輕代、老年代統一回收;大部分時候,回收都是在年輕代GC分為兩種:輕GC(清理年輕代)重GC(清理年輕代+老年代)2、GC演算法1、引用計數演算法(很少使用)每個物件在建立的時候,就給這個物件繫結一個計數器。每當有一個引用指向該物件時,計數器加一;每當有一個指向它的引用被刪除時,計數器減一。這樣,當沒有引用指向該物件時,該物件死亡,計數器為0,這時就應該對這個物件進行垃圾回收操作。2、複製演算法複製演算法主要發生在年輕代( 倖存0區 和 倖存1區)
當Eden區滿的時候,會觸發輕GC,每觸發一次,活的物件就被轉移到倖存區,死的就被GC清理掉了,所以每觸發輕GC時,Eden區就會清空;物件被轉移到了倖存區,倖存區又分為From Space和To Space,這兩塊區域是動態交換的,誰是空的誰就是To Space,然後From Space就會把全部物件轉移到To Space去;那如果兩塊區域都不為空呢?這就用到了複製演算法,其中一個區域會將存活的物件轉移到另一個區域去,然後將自己區域的記憶體空間清空,這樣該區域為空,又成為了To Space;所以每次觸發輕GC後,Eden區清空,同時To區也清空了,所有的物件都在From區這也就是倖存0區和倖存1區總有一塊為空的原因
好處:沒有記憶體的碎片(記憶體集中在一塊)
壞處:
浪費了記憶體空間(浪費了倖存區一半空間)物件存活率較高的場景下(比如老年代那樣的環境),需要複製的東西太多,效率會下降。最佳使用環境:物件存活度較低的時候,也就是年輕代
3、標記–清除演算法為每個物件儲存一個標記位,記錄物件的生存狀態
標記階段:這個階段內,為每個物件更新標記位,檢查物件是否死亡;清除階段:該階段對死亡的物件進行清除,執行 GC 操作。缺點:兩次掃描嚴重浪費時間,會產生記憶體碎片
優點:不需要額外的空間
4、標記–整理演算法標記-整理法 是 標記-清除法 的一個改進版。
又叫做 標記-清除-壓縮法
標記階段,該演算法也將所有物件標記為存活和死亡兩種狀態;不同的是,在第二個階段,該演算法並沒有直接對死亡的物件進行清理,而是將所有存活的物件整理一下,放到另一處空間,然後把剩下的所有物件全部清除。可以進一步最佳化,在記憶體碎片不太多的情況下,就繼續標記清除,到達一定量的時候再壓縮.
總結記憶體(時間複雜度)效率:複製演算法 > 標記清除演算法 > 標記壓縮演算法
記憶體整齊度:複製演算法 = 標記壓縮法 > 標記清除法
記憶體利用率:標記壓縮法 = 標記清除法 > 複製演算法
思考:有沒有最優的演算法?沒有最優的演算法,只有最合適的演算法
GC 也稱為 分代收集演算法
對於年輕代:
物件存活率低用複製演算法對於老年代: