JVM 内存区域
java運行時數據區域
1.程序計數器
程序計數器是一塊較小的內存空間,它可以看做是當前線程所執行的字節碼的行號指示器。
程序計數器是線程獨立的,每個線程擁有獨立的程序計數器。
程序計數器內存區域是唯一一個在java虛擬機規范中沒有規定任何OutOfMemoryError情況的區域
2.java虛擬機棧
a.線程私有,與線程的生命周期相同。
b.每個方法執行時都創建一個棧幀用于存儲局部變量表、操作數棧、動態鏈接、方法出口等信息
局部變量表:存放編譯期可知的各種基本數據類型、對象引用(reference類型)和returnAddress類型(指向了一條字節碼指令的地址)。
64位長度的long和double類型的數據會占用2個局部變量空間,其余的數據類型占用1個。
c.有兩種異常:如果線程請求的棧深度大于虛擬機所允許的深度,拋出StackOverflowError異常;
如果虛擬機棧可以動態擴展,擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。
3.本地方法棧
功能與java虛擬機棧相似,區別:虛擬機棧為虛擬機執行java方法服務,而本地方法棧則為虛擬機使用到的Native方法服務。
4.Java堆
a.被所有線程共享的一塊內存區域,用于存放對象實例。
b.Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱為“GC堆”
c.Java堆中可以細分為:新生代和老年代。
d.線程共享的Java堆中可能劃分出多個線程私有的分配緩沖區(TLAB)。
e.Java虛擬機規范規定:Java堆可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可。
?
Java堆區的劃分:Eden Space(伊甸園)、Survivor Space(幸存者區)、Tenured Gen(老年代-養老區)。
HotSpot虛擬機GC算法采用分代收集算法:
1、一個人(對象)出來(new 出來)后會在Eden Space(伊甸園)無憂無慮的生活,直到GC到來打破了他們平靜的生活。
GC會逐一問清楚每個對象的情況,有沒有錢(此對象的引用)啊,因為GC想賺錢呀,有錢的才可以敲詐嘛。
然后富人就會進入Survivor Space(幸存者區),窮人的就直接kill掉。
2、并不是進入Survivor Space(幸存者區)后就保證人身是安全的,但至少可以活段時間。GC會定期(可以自定義)
會對這些人進行敲詐,億萬富翁每次都給錢,GC很滿意,就讓其進入了Genured Gen(養老區)。萬元戶經不住幾次敲詐
就沒錢了,GC看沒有啥價值啦,就直接kill掉了。
3、進入到養老區的人基本就可以保證人身安全啦,但是億萬富豪有的也會揮霍成窮光蛋,只要錢沒了,GC還是kill掉。
分區的目的:新生區由于對象產生的比較多并且大都是朝生夕滅的,所以直接采用標記-清理算法。而養老區生命力很強,
則采用復制算法,針對不同情況使用不同算法。
非heap區域中Perm Gen中放著類、方法的定義,jvm Stack區域放著方法參數、局域變量等的引用,方法執行順序按照棧的先入后出方式。
以上轉自:http://lhc1986.iteye.com/blog/1421832
以下轉自:http://www.cnblogs.com/xhr8334/archive/2011/12/01/2270994.html
?
5.方法區
a.線程共享的,用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。
b.通常稱為“永久代”。
6.運行時常量池
a.運行時常量池是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放。
7.直接內存
?
GC工作機制
?
SUN的jvm內存池被劃分為以下幾個部分:
Eden?Space (heap)
內存最初從這個線程池分配給大部分對象。
?
Survivor Space (heap)
用于保存在eden space內存池中經過垃圾回收后沒有被回收的對象。
?
Tenured Generation (heap)
用于保持已經在survivor space內存池中存在了一段時間的對象。
?
Permanent Generation (non-heap)
保存虛擬機自己的靜態(reflective)數據,例如類(class)和方法(method)對象。Java虛擬機共享這些類數據。
這個區域被分割為只讀的和只寫的。
?
Code Cache (non-heap)
HotSpot Java虛擬機包括一個用于編譯和保存本地代碼(native code)的內存,叫做“代碼緩存區”(code cache)。
?
簡單來講,jvm的內存回收過程是這樣的:
對象在Eden Space創建,當Eden Space滿了的時候,gc就把所有在Eden Space中的對象掃描一次,把所有有效的對象復制到第一個Survivor Space,同時把無效的對象所占用的空間釋放。當Eden Space再次變滿了的時候,就啟動移動程序把Eden Space中有效的對象復制到第二個Survivor Space,同時,也將第一個Survivor Space中的有效對象復制到第二個Survivor Space。如果填充到第二個Survivor Space中的有效對象被第一個Survivor Space或Eden Space中的對象引用,那么這些對象就是長期存在的,此時這些對象將被復制到Permanent Generation。
若垃圾收集器依據這種小幅度的調整收集不能騰出足夠的空間,就會運行Full GC,此時jvm gc停止所有在堆中運行的線程并執行清除動作。
轉至:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29632145&id=4616836
?
虛擬機對象
1.對象的創建
虛擬機遇到new指令時,
a.類加載:
檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執行相應的類加載過程
b.分配內存:為新生對象分配內存,等同于把一塊確定大小的內存從Java堆中劃分出來。
分配策略:
如果Java堆中內存是絕對規整的,所有用過的內存都放在一邊,空閑的內存放到另一邊,中間放著一個指針作為分界點的指示器,那所分配內存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相同的距離。這種分配方式稱為“指針碰撞”。
如果Java堆中的內存并不是規整的,已使用的內存和空閑的內存相互交錯,虛擬機就必須維護一個列表,記錄上哪些內存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,并更新列表上的記錄,這種分配方式稱為“空閑列表”。
注:在劃分可用空間的時候,可能出現線程安全問題。由于Java堆是線程共享的,因此在創建多個對象的時候會出現線程安全的問題。解決方法:對分配內存空間的動作進行同步處理;把內存分配的動作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊內存,稱為本地線程分配緩沖(TLAB)。虛擬機是否使用TLAB,可以通過 ?-XX:+/-UseTLAB參數來設定。
c.初始化:將分配到的內存空間都初始化為零值。
d.執行<init>方法:執行<init>方法,把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全出來。
2.對象的內存布局
對象在內存中存儲的布局可以分為3塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding).
注:對齊填充不是必然存在的。HotSpot VM的自動內存管理系統要求對象起始地址必須是8字節的整數倍,如果對象實例數據部分沒有對齊時,就需要通過對齊填充來補全。
3.對象的訪問定位
Java程序需要通過棧上的reference數據來操作堆上的具體對象。由于reference類型在Java虛擬機規范中規定了一個紙箱對象的引用,并沒有定義這個引用應該通過何種方式去定位。
目前主流的訪問方式有使用句柄和直接指針兩種:
如果使用句柄訪問的話,那么Java堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據和類型數據各自的具體地址信息。
如果使用直接指針訪問,那么Java堆對象的布局中就必須考慮如何放置訪問類型數據的相關信息,而reference中存儲的直接就是對象地址。
兩者各有優勢:
句柄訪問的最大好處就是reference中存儲的是穩定的句柄地址,在對象被移動時只會改變句柄中的數據指針,而reference本身不需要修改。
使用直接指針訪問方式最大的好處就是更快。
?
轉載于:https://www.cnblogs.com/lyf906522290/p/8043369.html
總結
- 上一篇: idea快捷键操作
- 下一篇: Jaspersoft Studio简介