1、棧是線程私有的,棧的生命周期和線程一樣,每個方法在執行的時候就會建立一個棧幀,它包含局部變量表、操作數棧、動态連結、方法出口等資訊,局部變量表又包括基本資料類型和對象的引用;
2、當線程請求的棧深度超過了虛拟機允許的最大深度時,會抛出StackOverFlowError異常,方法遞歸調用肯可能會出現該問題;
3、調整參數-xss去調整jvm棧的大小
jvm将虛拟機分為5大區域,程式計數器、虛拟機棧、本地方法棧、java堆、方法區;
程式計數器:線程私有的,是一塊很小的記憶體空間,作為目前線程的行号訓示器,用于記錄目前虛拟機正在執行的線程指令位址;
虛拟機棧:線程私有的,每個方法執行的時候都會建立一個棧幀,用于存儲局部變量表、操作數、動态連結和方法傳回等資訊,當線程請求的棧深度超過了虛拟機允許的最大深度時,就會抛出StackOverFlowError;
本地方法棧:線程私有的,儲存的是native方法的資訊,當一個jvm建立的線程調用native方法後,jvm不會在虛拟機棧中為該線程建立棧幀,而是簡單的動态連結并直接調用該方法;
堆:java堆是所有線程共享的一塊記憶體,幾乎所有對象的執行個體和數組都要在堆上配置設定記憶體,是以該區域經常發生垃圾回收的操作;
方法區:存放已被加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼資料。即永久代,在jdk1.8中不存在方法區了,被中繼資料區替代了,原方法區被分成兩部分;1:加載的類資訊,2:運作時常量池;加載的類資訊被儲存在中繼資料區中,運作時常量池儲存在堆中;
java堆 = 新生代+老年代;
新生代 = Eden + Suivivor(S0 + S1),預設配置設定比例是8:1:1;
當Eden區空間滿了的時候,就會觸發一次Minor GC,以收集新生代的垃圾,存活下來的對象會被配置設定到Survivor區
大對象(需要大量連續記憶體空間的對象)會直接被配置設定到老年代
如果對象在Eden中出生,并且在經曆過一次Minor GC之後仍然存活,被配置設定到存活區的話,年齡+1,此後每經曆過一次Minor GC并且存活下來,年齡就+1,當年齡達到15的時候,會被晉升到老年代;
當老年代滿了,而無法容納更多對象的話,會觸發一次full gc;full gc存儲的是整個記憶體堆(包括年輕代和老年代);;
Major GC是發生在老年代的GC,清理老年區,經常會伴随至少一次minor gc;
java中有四種垃圾回收算法,分别是标記清除法、标記整理法、複制算法、分代收集算法;
标記清除法:
第一步:利用可達性去周遊記憶體,把存活對象和垃圾對象進行标記;
第二步:在周遊一遍,将所有标記的對象回收掉;
特點:效率不行,标記和清除的效率都不高;标記和清除後會産生大量的不連續的空間分片,可能會導緻之後程式運作的時候需配置設定大對象而找不到連續分片而不得不觸發一次GC;
标記整理法:
第二步:将所有的存活的對象向一段移動,将端邊界以外的對象都回收掉;
特點:适用于存活對象多,垃圾少的情況;需要整理的過程,無空間碎片産生;
複制算法:
将記憶體按照容量大小分為大小相等的兩塊,每次隻使用一塊,當一塊使用完了,就将還存活的對象移到另一塊上,然後在把使用過的記憶體空間移除;
特點:不會産生空間碎片;記憶體使用率極低;
分代收集算法:
根據記憶體對象的存活周期不同,将記憶體劃分成幾塊,java虛拟機一般将記憶體分成新生代和老生代,在新生代中,有大量對象死去和少量對象存活,是以采用複制算法,隻需要付出少量存活對象的複制成本就可以完成收集;老年代中因為對象的存活率極高,沒有額外的空間對他進行配置設定擔保,是以采用标記清理或者标記整理算法進行回收;
判斷一個對象是否存活,分為兩種算法1:引用計數法;2:可達性分析算法;
引用計數法:
給每一個對象設定一個引用計數器,當有一個地方引用該對象的時候,引用計數器就+1,引用失效時,引用計數器就-1;當引用計數器為0的時候,就說明這個對象沒有被引用,也就是垃圾對象,等待回收;
缺點:無法解決循環引用的問題,當A引用B,B也引用A的時候,此時AB對象的引用都不為0,此時也就無法垃圾回收,是以一般主流虛拟機都不采用這個方法;
可達性分析法
從一個被稱為GC Roots的對象向下搜尋,如果一個對象到GC Roots沒有任何引用鍊相連接配接時,說明此對象不可用,在java中可以作為GC Roots的對象有以下幾種:
虛拟機棧中引用的對象
方法區類靜态屬性引用的變量
方法區常量池引用的對象
本地方法棧JNI引用的對象
但一個對象滿足上述條件的時候,不會馬上被回收,還需要進行兩次标記;第一次标記:判斷目前對象是否有finalize()方法并且該方法沒有被執行過,若不存在則标記為垃圾對象,等待回收;若有的話,則進行第二次标記;第二次标記将目前對象放入F-Queue隊列,并生成一個finalize線程去執行該方法,虛拟機不保證該方法一定會被執行,這是因為如果線程執行緩慢或進入了死鎖,會導緻回收系統的崩潰;如果執行了finalize方法之後仍然沒有與GC Roots有直接或者間接的引用,則該對象會被回收;
垃圾回收器主要分為以下幾種:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;
Serial:
單線程的收集器,收集垃圾時,必須stop the world,使用複制算法。
ParNew:
Serial收集器的多線程版本,也需要stop the world,複制算法.
Parallel Scavenge:
新生代收集器,複制算法的收集器,并發的多線程收集器,目标是達到一個可控的吞吐量,和ParNew的最大差別是GC自動調節政策;虛拟機會根據系統的運作狀态收集性能監控資訊,動态設定這些參數,以提供最優停頓時間和最高的吞吐量;
Serial Old:
Serial收集器的老年代版本,單線程收集器,使用标記整理算法。
Parallel Old:
是Parallel Scavenge收集器的老年代版本,使用多線程,标記-整理算法。
CMS:
是一種以獲得最短回收停頓時間為目标的收集器,标記清除算法,運作過程:初始标記,并發标記,重新标記,并發清除,收集結束會産生大量空間碎片;
G1:
标記整理算法實作,運作流程主要包括以下:初始标記,并發标記,最終标記,篩選回收。不會産生空間碎片,可以精确地控制停頓;
G1将整個堆分為大小相等的多個Region(區域),G1跟蹤每個區域的垃圾大小,在背景維護一個優先級清單,每次根據允許的收集時間,優先回收價值最大的區域,已達到在有限時間内擷取盡可能高的回收效率;
虛拟機把描述類的資料加載到記憶體裡面,并對資料進行校驗、解析和初始化,最終變成可以被虛拟機直接使用的class對象;
主要分為以下幾個過程:加載、驗證、準備、解析、初始化;
加載:
加載分為三步:
1、通過類的全限定性類名擷取該類的二進制流;
2、将該二進制流的靜态存儲結構轉為方法區的運作時資料結構;
3、在堆中為該類生成一個class對象;
驗證:
驗證該class檔案中的位元組流資訊複合虛拟機的要求,不會威脅到jvm的安全;
準備:
為class對象的靜态變量配置設定記憶體,初始化其初始值;
解析:
該階段主要完成符号引用轉化成直接引用;
初始化:
到了初始化階段,才開始執行類中定義的java代碼;
初始化階段是調用類構造器的過程;
類加載器是指:通過一個類的全限定性類名擷取該類的二進制位元組流叫做類加載器;
類加載器分為以下四種:
啟動類加載器:
用來加載java核心類庫,無法被java程式直接引用;
擴充類加載器:
用來加載java的擴充庫,java的虛拟機實作會提供一個擴充庫目錄,該類加載器在擴充庫目錄裡面查找并加載java類;
系統類加載器:
它根據java的類路徑來加載類,一般來說,java應用的類都是通過它來加載的;
自定義類加載器:
由java語言實作,繼承自ClassLoader;
當一個類加載器收到一個類加載的請求,他首先不會嘗試自己去加載,而是将這個請求委派給父類加載器去加載,隻有父類加載器在自己的搜尋範圍類查找不到給類時,子加載器才會嘗試自己去加載該類;
為了防止記憶體中出現多個相同的位元組碼;
因為如果沒有雙親委派的話,使用者就可以自己定義一個java.lang.String類,那麼就無法保證類的唯一性;
自定義類加載器,繼承ClassLoader類,重寫loadClass方法和findClass方法;
強引用:強引用是我們使用最廣泛的引用,如果一個對象具有強引用,那麼垃圾回收期絕對不會回收它,當記憶體空間不足時,垃圾回收器甯願抛出OutOfMemoryError,也不會回收具有強引用的對象;我們可以通過顯示的将強引用對象置為null,讓gc認為該對象不存在引用,進而來回收它;
軟引用:軟應用是用來描述一些有用但不是必須的對象,在java中用SoftReference來表示,當一個對象隻有軟應用時,隻有當記憶體不足時,才會回收它;
軟引用可以和引用隊列聯合使用,如果軟引用所引用的對象被垃圾回收器所回收了,虛拟機會把這個軟引用加入到與之對應的引用隊列中;
弱引用:弱引用是用來描述一些可有可無的對象,在java中用WeakReference來表示,在垃圾回收時,一旦發現一個對象隻具有軟引用的時候,無論目前記憶體空間是否充足,都會回收掉該對象;
弱引用可以和引用隊列聯合使用,如果弱引用所引用的對象被垃圾回收了,虛拟機會将該對象的引用加入到與之關聯的引用隊列中;
虛引用:虛引用就是一種可有可無的引用,無法用來表示對象的生命周期,任何時候都可能被回收,虛引用主要使用來跟蹤對象被垃圾回收的活動,虛引用和軟引用與弱引用的差別在于:虛引用必須和引用隊列聯合使用;在進行垃圾回收的時候,如果發現一個對象隻有虛引用,那麼就會将這個對象的引用加入到與之關聯的引用隊列中,程式可以通過發現一個引用隊列中是否已經加入了虛引用,來了解被引用的對象是否需要被進行垃圾回收;