Ⅰ jvm堆內存和非堆內存(小白入門文,各博客視頻基礎總結)
一:堆內存和非堆內存定義
java虛擬機具有一個堆(Heap),堆是運行時數據區域,所有類實例和數組的內存均從此處分配。堆是Java虛擬機啟動時創建的。在JVM中堆之外的內u你成為非堆內存(Non-heap memory)。
堆內存以及相應垃圾回收演算法
1.堆的大小可以固定,也可以擴大和縮小,堆內存不需要是連續空間。
2.對象創建後進入Eden。年輕代分為Eden和Survivor。Survivor由FromSpace和ToSpace組成。Eden區佔大容量,Survivor佔小容量,默認比例8:1:1。
MinorGC:採用復制演算法。首先把Eden和ServivorFrom區域中存活的對象賦值到ServivorTo區域(如果對象年齡達到老年標准/ServivorTo位置不夠了,則復制到老年代),同時對象年齡+1,然後清空Eden和ServivorFrom中的對象。然後ServivorTo和ServivorFrom互換。
3.老年代
老年代存放生命周期長的內存對象。
老年代對象相對穩定,所以不會頻繁GC。在進行MajorGC前一般都先進行一次MinorGC,使新生代的對象進入老年代,導致空間不夠用時才觸發。當無法找到足夠大的連續空間分配給新晉的對象也會提前觸發MajorGC進行垃圾回收。
MajorGC:如果使用CMS收集器,採用標記-清除演算法。首先掃描老年代,標記游笑孝所有可回收對象,標記完成後統一回收所有被標記對象。同時會產生不連續的內存碎片。碎片過多會導致以後程序運行需要分配較大對象時,無法找到足夠的連續內存,而不得已再次出發GC。否則採用標記-壓縮演算法。
標記-壓縮:在標記可回收對象後,將不可回收對象移向一端,然後清除標記對象。
當神稿老年代也滿了裝不下時,拋出OOM異常。
二:永久代
內存中永久保存的區域,主要存放Class和Meta(元數據)的信息,Class在被載入的時候被放入永久區域。他和存放實例的區域不同,GC不會再主程序運行期對永久區進行清理。所以也可可能導致永久代區域隨著載入Class的增多而脹滿,拋出OOM。
Java8中,永久代已經被移除,被一個成為「元數據區」(元空間)的區域所取代。
元空間的本質與永久代類似,都是JVM方法區的實現。不過元空間使用本地內存,永久代在JVM虛擬機中。因此,默認情況下,元空間的大小受本地內存限制。類的元數據放入native memory,字元串常量池和類的靜態變數放入java堆中,這樣可以載入多少類的元數據就不再由MaxPermSize控制,而是由系統實際可用空間控制。
1元空間解決了永久代的OOM問題,元數據和class對象在永久代容易出現性能問題和內存溢出。
2類的方法信息等比較難確定其大小,對於永久代的大小指定比較困難,小永久代溢出,大老年代溢出。
3永久代會為GC帶來不必要的復雜度,回收效率低。
三:堆內存參數調優
1.-Xms 設置初始分配內存大小,默認物理內存1/64
2.-Xmx 設置最大分升碰配內存,默認物理內存1/4
long maxMemory = Runtime.getRuntime().maxMemory(); long totalMemory = Runtime.getRuntime().totalMemory(); System.out.println("最大分配內存"+maxMemory/(double)1024/1024+"MB "+maxMemory/(double)1024/1024/1024+"GB"); System.out.println("默認分配內存"+totalMemory/(double)1024/1024+"MB "+totalMemory/(double)1024/1024/1024+"GB");
Ⅱ Java垃圾回收:GC在什麼時候對什麼做了什麼
GC在什麼時候對什麼做了什麼?
要回答這個問題,先了解下GC的發展史、jvm運行時數據區的劃分、jvm內存分配策略、jvm垃圾收集演算法等知識。
先說下jvm運行時數據的劃分,粗暴的分可以分為堆區(Heap)和棧區(Stack),但jvm的分法實際上比這復雜得多,大概分為下面幾塊:
1、程序計數器(Program Conuter Register)
程序計數器是一塊較小的內存空間,它是當前線程執行位元組碼的行號指示器,位元組碼解釋工作器就是通過改變這個計數器的值來選取下一條需要執行的指令。它是線程私有的內存,也是唯一一個沒有OOM異常的區域。
2、Java虛擬機棧區(Java Virtual Machine Stacks)
也就是通常所說的棧區,它描述的是Java方法執行的內存模型,每個方法被執行的時候都創建一個棧幀(Stack Frame),用於存儲局部變數表、操作數棧、動態鏈接、方法出口等。每個方法被調用到完成,相當於一個棧幀在虛擬機棧中從入棧到出棧的過程。此區域也是線程私有的內存,可能拋出兩種異常:如果線程請求的棧深度大於虛擬機允許的深度將拋出StackOverflowError;如果虛擬機棧可以動態的擴展,擴展到無法動態的申請到足夠的內存時會拋出OOM異常。
3、本地方法棧(Native Method Stacks)
本地方法棧與虛擬機棧發揮的作用非常相似,區別就是虛擬機棧為虛擬機執行Java方法,本地方法棧則是為虛擬機使用到的Native方法服務。
4、堆區(Heap)
所有對象實例和數組都在堆區上分配,堆區是GC主要管理的區域。堆區還可以細分為新生代、老年代,新生代還分為一個Eden區和兩個Survivor區。此塊內存為所有線程共享區域,當堆中沒有足夠內存完成實例分配時會拋出OOM異常。
5、方法區(Method Area)
方法區也是所有線程共享區,用於存儲已被虛擬機載入的類信息、常量、靜態變數、即時編譯後的代碼等數據。GC在這個區域很少出現,這個區域內存回收的目標主要是對常量池的回收和類型的卸載,回收的內存比較少,所以也有稱這個區域為永久代(Permanent Generation)的。當方法區無法滿足內存分配時拋出OOM異常。
6、運行時常量池(Runtime Constant Pool)
運行時常量池是方法區的一部分,用於存放編譯期生成的各種字面量和符號引用。
垃圾收集(Garbage Collection)並不是Java獨有的,最早是出現在Lisp語言中,它做的事就是自動管理內存,也就是下面三個問題:
1、什麼時候回收
2、哪些內存需要回收
3、如何回收
1、什麼時候回收?
上面說到GC經常發生的區域是堆區,堆區還可以細分為新生代、老年代,新生代還分為一個Eden區和兩個Survivor區。
1.1 對象優先在Eden中分配,當Eden中沒有足夠空間時,虛擬機將發生一次Minor GC,因為Java大多數對象都是朝生夕滅,所以Minor GC非常頻繁,而且速度也很快;
1.2 Full GC,發生在老年代的GC,當老年代沒有足夠的空間時即發生Full GC,發生Full GC一般都會有一次Minor GC。大對象直接進入老年代,如很長的字元串數組,虛擬機提供一個-XX:PretenureSizeThreadhold參數,令大於這個參數值的對象直接在老年代中分配,避免在Eden區和兩個Survivor區發生大量的內存拷貝;
1.3 發生Minor GC時,虛擬機會檢測之前每次晉升到老年代的平均大小是否大於老年代的剩餘空間大小,如果大於,則進行一次Full GC,如果小於,則查看HandlePromotionFailure設置是否允許擔保失敗,如果允許,那隻會進行一次Minor GC,如果不允許,則改為進行一次Full GC。
2、哪些內存需要回收
jvm對不可用的對象進行回收,哪些對象是可用的,哪些是不可用的?Java並不是採用引用計數演算法來判定對象是否可用,而是採用根搜索演算法(GC Root Tracing),當一個對象到GC Roots沒有任何引用相連接,用圖論的來說就是從GC Roots到這個對象不可達,則證明此對象是不可用的,說明此對象可以被GC。對於這些不可達對象,也不是一下子就被GC,而是至少要經歷兩次標記過程:如果對象在進行根搜索演算法後發現沒有與GC Roots相連接的引用鏈,那它將會第一次標記並且進行一次篩選,篩選條件是此對象有沒有必要執行finalize()方法,當對象沒有覆蓋finalize()方法或者finalize()方法已經被虛擬機調用執行過一次,這兩種情況都被視為沒有必要執行finalize()方法,對於沒有必要執行finalize()方法的將會被GC,對於有必要有必要執行的,對象在finalize()方法中可能會自救,也就是重新與引用鏈上的任何一個對象建立關聯即可。
3、如何回收
選擇不同的垃圾收集器,所使用的收集演算法也不同。
在新生代中,每次垃圾收集都發現有大批對象死去,只有少量存活,則使用復制演算法,新生代內存被分為一個較大的Eden區和兩個較小的Survivor區,每次只使用Eden區和一個Survivor區,當回收時將Eden區和Survivor還存活著的對象一次性的拷貝到另一個Survivor區上,最後清理掉Eden區和剛才使用過的Survivor區,Eden和Survivor的默認比例是8:1,可以使用-XX:SurvivorRatio來設置該比例。
而老年代中對象存活率高,沒有額外的空間對它進行分配擔保,必須使用「標記-清理」或「標記-整理」演算法。
Ⅲ 一、Android 虛擬機內存模型
jvm運行時數據區域解析
劉望舒的BLOG
虛擬機的內存模型
1.程序計數器: 確定程序指令執行順序的,是唯一一塊不會發生內存溢出的區域
2. Java虛擬機棧
**它也是線程私有的,負責存儲方法內的局部變數,方法出口等。每執行一個方法都相當於壓如一個棧幀,方法執行完比後這個棧幀從Java虛擬機棧中彈出。
3.本地方法區
負責管理虛擬機用到的 C 的方法。
4.堆內存區域
Java堆是一塊被所有線程共享的區域,用來存放對象的實例。它不需要物理上連續,只需要邏輯上連續就可以。
5.方法區
方法區是被所有線程共享的的內存區域,用來存放已經被Java虛擬機載入的類的結構信息:運行時常量池,欄位,方法信息,靜態變數等數據。
class文件的內容
Ⅳ 重新理解jvm運行時的內存分布(堆棧方法區交互)
棧堆方法區的交互關系
java棧存儲的本地變數表,包括八種數據類型和引用類型,引用類型指向對象的地址,保存在reference,指向java堆,對象類型數據會保存變數名,變數類型,變數值等,這些會存在方法區中去查看(在初始化的時候)。
在java棧中會存放對象實例(s1),但是他對象旁汪實例中具體的數據會由java棧中的引用指向java堆中的地址,裡面的對象實例數據存放(實例名,實例相關類型,元數據信息。。。。),而靜態變數,常量,類載入後的信息等會存放在方法區,在運行時需要調用的時候去方法區取,所以方法區和java堆都是共享的。而java棧時線程獨有的數據(包括程序計數器,本地方法棧)。
一個jvm實例,只存在一個堆內存,堆內存的大小是可以調節的。類載入器讀取了類文件之後,需要把類,方法,常量放到堆內存中,保存所有的引用類型的真實信息,以方便執行器執行。堆內存分為三部分。
(養老區就是老年代)
堆內存 邏輯上 分為三部:新生 +養老 +方法區
eden+survivor+Spaces(元空間或者叫方法區或者Perm)
Perm 永久存儲區,是一個常駐內存的區域,用於存放jdk自身攜帶的Class,Interface的元數據,被裝載進此區域的數據是不會被垃圾回收器回收的,只有關閉jvm後才會釋放此區域所佔用的內存。
如果出現OutOfMemoryReeor: PermGen space 說明java虛擬機堆永久帶Perm內存設置不夠,答襪一半出現這種情況,都是程序啟動載入大量第三方jar呆滯的,
對於HotSpot虛擬機很多開發者習慣將方法區稱之為永久代(Parmenent
Gen),永久代是方法區的一個實現,這是不對的,方法區是邏輯上的部分。在jdk7中已經將原本放在永久代的字元串常量池移走了。
常量池( Constant Pool Constant PoolConstant Pool Constant Pool Constant Pool )是方法區的一部分, Class Class文件除了有類的版本、 欄位方法、介面等描述信息外,還有一項就是常量池這部分內容將在類載入後進入。
伊甸園區,所有對象剛new出來都會放在這里。
對象分兩種:
1.如果是大對象直接分配在Old區。
2.如果禁言了逃逸分析,會運舉仔在棧上分配。
以上兩種都不符合,放入伊甸園區。(Eden區)
看java7中如圖:
對比java8