柚子快報激活碼778899分享:JVM專題
柚子快報激活碼778899分享:JVM專題
虛擬機
虛擬機及其構(gòu)成
虛擬機指通過軟件模擬的具有完整硬件系統(tǒng)功能的、運行在一個完全隔離環(huán)境中的完整計算機系統(tǒng)。Java虛擬機對字節(jié)碼進行解釋生成對應平臺的機器碼并執(zhí)行。Java虛擬機是Java跨平臺的重要原因。
Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。
主要包括類加載器、運行時數(shù)據(jù)區(qū)、執(zhí)行引擎、本地方法接口、本地方法庫、垃圾回收器。其中重點關(guān)注的運行時數(shù)據(jù)區(qū),又可細分為方法區(qū)、堆區(qū)、虛擬機棧、本地方法棧、程序計數(shù)器,其中方法區(qū)和堆區(qū)為進程的所有子線程共享,其它的為線程獨有。
類加載器
負責把class文件裝載到內(nèi)存中,供虛擬機執(zhí)行。
執(zhí)行引擎
負責執(zhí)行字節(jié)碼。
本地方法庫
本地方法(native方法)就是一個Java調(diào)用非Java代碼的接口。一個native方法是指該方法的實現(xiàn)由非Java語言實現(xiàn),比如用C或C++實現(xiàn)。
本地方法庫接口
允許Java代碼使用以其他語言編寫的native本地方法庫。
垃圾回收器
回收無用對象所占用的內(nèi)存空間。
運行時數(shù)據(jù)區(qū)
JVM的核心區(qū)域,程序運行時的工作區(qū)域,數(shù)據(jù)加工處理的場所。下面進行重點說明。
運行時數(shù)據(jù)區(qū)
程序計數(shù)器
程序計數(shù)器是一塊較小的內(nèi)存空間,它的作用可以看作是當前線程所執(zhí)行的字節(jié)碼的行號指示器。每個線程都有一個獨立的程序計數(shù)器,各條線程之間的計數(shù)器互不影響,獨立存儲,是線程私有內(nèi)存。如果正在執(zhí)行Native方法,則這個計數(shù)器為空。
Java虛擬機棧(Java Virtal Machine Stack)
同樣也是屬于線程私有區(qū)域,每個線程在創(chuàng)建的時候都會創(chuàng)建一個虛擬機棧,生命周期與線程一致,線程退出時,線程的虛擬機棧也回收。虛擬機棧內(nèi)部保持一個個的棧幀,每次方法調(diào)用都會進行壓棧,JVM對棧幀的操作只有出棧和壓棧兩種,方法調(diào)用結(jié)束時會進行出棧操作。該區(qū)域存儲著局部變量表,編譯時期可知的各種基本類型數(shù)據(jù)、對象引用、方法出口等信息。
本地方法棧(Native Method Stack)
與虛擬機棧類似,本地方法棧是在調(diào)用本地方法時使用的棧,每個線程都有一個本地方法棧。
Java堆(heap)
Java堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱為GC堆,Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建。此區(qū)域的唯一目的就是存放對象實例,幾乎所有對象的實例都在這分配內(nèi)存。
方法區(qū)
方法區(qū)和Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機加載的類信息(Class對象,反射機制中重點講授),常量,靜態(tài)變量,即時編譯器編譯后的代碼等數(shù)據(jù)。
方法區(qū)是一種Java虛擬機的規(guī)范。由于方法區(qū)存儲的數(shù)據(jù)和堆中存儲的數(shù)據(jù)一致,實質(zhì)上也是堆,因此,在不同的JDK版本中方法區(qū)的實現(xiàn)方式不一樣。
堆內(nèi)存
堆內(nèi)存分為三部分
1.年輕代:Young
2.老年代:Tenured(Old)
3.永久代 PermGen? JDK8中變成元空間MetaSpace
擴展:內(nèi)存泄漏和內(nèi)存溢出 內(nèi)存泄漏(Memory Leak):對象所要做的事情完成了,希望他們會被回收掉。但如果還有對這個對象的其他引用,它是不會被回收的,它還會占用內(nèi)存。持續(xù)累加,內(nèi)存很快被耗盡。 內(nèi)存溢出(out of memory):指程序申請內(nèi)存時,沒有足夠的內(nèi)存供申請者使用。
方法區(qū)是虛擬機提出的一個規(guī)范。在具體的虛擬機中,比如我們常用的是Sun公司提供的Hotspot虛擬機,它是將堆中的永久代作為方法區(qū)去實現(xiàn)的。當然了,在JDK8.0及以后堆中沒有永久代了,使用了元空間來替代。元空間和之前永久代相比,元空間使用的是物理內(nèi)存,這樣的話它存儲的內(nèi)容就會多很多。很少能夠發(fā)生堆內(nèi)存溢出。
垃圾回收
概述
Java引入了垃圾回收機制(Garbage Collection),令C++程序員最頭疼的內(nèi)存管理問題迎刃而解。Java程序員可以將更多的精力放到業(yè)務邏輯上而不是內(nèi)存管理工作上,大大的提高了開發(fā)效率。
不同類型的、不同版本的虛擬機有不同的垃圾回收器。各種垃圾回收器的回收算法也會有不同,表現(xiàn)各異。此處講解一種分代垃圾回收機制。
為什么要回收垃圾
堆內(nèi)存是用來存放對象和數(shù)據(jù)的。
如果我們創(chuàng)建的對象那個沒有得到及時回收,持續(xù)堆積,最后會導致堆空間被占滿,內(nèi)存溢出。
java給我們提供了垃圾回收機制,在后臺有一個守護進程,它會把堆空間中的垃圾進行回收,從而保證程序的正常運行。
怎樣判斷是垃圾
什么是垃圾呢?
所謂“垃圾”,就是指不用的對象,或者說不需要存活的對象。
常見的判斷是否存活有兩種方法:1.引用計數(shù)法??? 2.可達性分析
引用計數(shù)法:
為每一個創(chuàng)建的對象分配一個引用計數(shù)器,用來存儲該對象被引用的個數(shù)。 當該個數(shù)為0,意味著沒有人引用該對象,就可以認為“對象死亡”。 但是,這種方案存在嚴重的問題,就是無法檢測“循環(huán)引用”:比如,當兩個對象互相引用時,即使他倆都不被外界其他任何東西引用,它倆的引用計數(shù)都不為0,因此永遠不會被回收。而實際上對于開發(fā)者而言,這兩個對象已經(jīng)完全沒有用處了。? 因此,java里沒有采用這樣的方案來判定對象的“存活性”(Python采用的是引用計數(shù)法)。
public class Test1 {
??? public static void main(String[] args) {
??????? MyObject object1 = new MyObject(); ??????? MyObject object2 = new MyObject(); ??????? object1.object = object2; ??????? object2.object = object1; ??????? object1 = null; ??????? object2 = null; ??? } }class MyObject{
??? public Object object = null; }
最后面兩句將object1和object2賦值為null,也就是說object1和object2指向的對象已經(jīng)不可能再被訪問,但是由于它們互相引用對方,導致它們的引用計數(shù)都不為0,那么垃圾收集器就永遠不會回收它們。
可達性分析:
這種方案是目前主流語言里采用的對象存活性判斷方案。 基本思路是把所有引用的對象想象成一棵樹,從樹的根節(jié)點GC Roots出發(fā),持續(xù)遍歷找出所有連接的樹枝對象,這些對象被稱為“可達”對象,或“存活”對象。其余的對象則被視為“死亡”的“不可達”對象,或稱為“垃圾”。
參考下圖,object5,object6,object7便是不可達對象,視為“死亡狀態(tài)”,應該被垃圾回收器回收。
GC Roots是什么呢?
我們可以確定,GC Roots本身一定是可達的,這樣從它們出發(fā)遍歷到的對象才能保證一定可達。 java中有哪些對象是一定可達的呢?主要有以下4種:
1. 虛擬機棧中引用的對象
2. 方法區(qū)中靜態(tài)屬性引用的對象
3. 方法區(qū)中常量引用的對象
4. 本地方法棧中JNI引用的對象
大家需要知道有這么幾種類型的GC Roots,每次垃圾回收器都會從這些根節(jié)點開始遍歷尋找所有可達節(jié)點。
垃圾回收方式
上面已經(jīng)知道,所有GC Roots不可達的對象都稱為垃圾,參考下圖,黑色的表示垃圾,灰色的表示存活對象,綠色表示空白空間。
方式一:標記-清理
第一步:所謂“標記”就是利用可達性遍歷堆內(nèi)存,把“存活”對象和“垃圾”對象進行標記,得到的結(jié)果如上圖;
第二步:既然“垃圾”已經(jīng)標記好了,那我們就再遍歷一遍,把所有“垃圾”對象所占的空間直接清空即可。結(jié)果如下:
這就是 標記-清理 方法,簡單方便,但是容易產(chǎn)生內(nèi)存碎片。
方式二:標記-整理
既然上面的方法會產(chǎn)生內(nèi)存碎片,那么在清理的時候,把所有存活的對象扎堆到一塊,這樣就沒有內(nèi)存碎片了。
結(jié)果如下:
這種方法適合存活對象多,垃圾少的情況,它只需要清理掉少量的垃圾,然后挪動下存活對象就可以了。
方式三:復制
這種方法比較簡單粗暴,直接把堆內(nèi)存分成兩部分,一段時間內(nèi)只允許在其中一塊內(nèi)存上進行分配,當這塊
柚子快報激活碼778899分享:JVM專題
文章鏈接
本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。