Java虛擬機詳述

一有巧、JVM內(nèi)存區(qū)域模型

JVM內(nèi)存區(qū)域模型

1.堆

也叫做java 堆鹉胖、GC堆是java虛擬機所管理的內(nèi)存中最大的一塊內(nèi)存區(qū)域,也是被各個線程共享的內(nèi)存區(qū)域绒尊,在JVM啟動時創(chuàng)建畜挥。

該內(nèi)存區(qū)域存放了對象實例及數(shù)組(所有new的對象),每個對象都包含一個與之對應的class的信息婴谱。(class的目的是得到操作指令)

其大小通過-Xms(最小值)和-Xmx(最大值)參數(shù)設置蟹但,-Xms為JVM啟動時申請的最小內(nèi)存,默認為操作系統(tǒng)物理內(nèi)存的1/64但小于1G谭羔,-Xmx為JVM可申請的最大內(nèi)存华糖,默認為物理內(nèi)存的1/4但小于1G,默認當空余堆內(nèi)存小于40%時瘟裸,JVM會增大Heap到-Xmx指定的大小客叉,可通過-XX:MinHeapFreeRation=來指定這個比列;當空余堆內(nèi)存大于70%時话告,JVM會減小heap的大小到-Xms指定的大小兼搏,可通過XX:MaxHeapFreeRation=來指定這個比列,對于運行系統(tǒng)沙郭,為避免在運行時頻繁調(diào)整Heap的大小佛呻,通常-Xms與-Xmx的值設成一樣。

由于現(xiàn)在收集器都是采用分代收集棠绘,堆被劃分為新生代和老年代件相。新生代主要存儲新創(chuàng)建的對象和尚未進入老年代的對象。老年代存儲經(jīng)過多次新生代GC(Minor GC)任然存活的對象氧苍。

新生代:

程序新創(chuàng)建的對象都是從新生代分配內(nèi)存夜矗,新生代由Eden Space和兩塊相同大小的Survivor Space(From Survivor Space 和 To Survivor Space)構成,可通過-Xmn參數(shù)來指定新生代的大小让虐,也可以通過-XX:SurvivorRation來調(diào)整Eden Space及Survivor Space的大小紊撕。

由于新生代采用復制算法進行垃圾回收,根據(jù)IBM研究表明新生代中的對象98%是“朝生夕死”的赡突,所以并不需要按照1:1的比例來劃分內(nèi)存空間对扶,而是將內(nèi)存分為較大的Eden Space和兩塊較小的Survivor Space区赵,每次使用Eden和其中一塊Survivor。當回收時浪南,將Eden和Survivor中還存活的對象復制到另一塊Survivor笼才,最后清理掉Eden和剛才用過的Survivor。

如果-XX:SurvivorRation=8則表示Eden空間和其中一塊Survivor空間大小比是8:1络凿,也就是說新生代的可用空間是整個新生代容量的90%(80%+10%)骡送,只有10%的空間會浪費。

老年代:

用于存放經(jīng)過多次新生代GC任然存活的對象絮记,例如緩存對象摔踱,新建的對象也有可能直接進入老年代,主要有兩種情況:
(1)大對象怨愤,可通過啟動參數(shù)設置-XX:PretenureSizeThreshold=1024(單位為字節(jié)派敷,默認為0)來代表超過多大時就不在新生代分配,而是直接在老年代分配
(2)大數(shù)組對象撰洗,且數(shù)組中無引用外部對象

老年代所占的內(nèi)存大小為-Xmx對應的值減去-Xmn對應的值篮愉。

2. 方法區(qū)

也稱"永久代” 、“非堆”了赵, 它用于存儲虛擬機加載的類信息潜支、常量、靜態(tài)變量柿汛、是各個線程共享的內(nèi)存區(qū)域。默認最小值為16MB埠对,最大值為64MB络断,可以通過-XX:PermSize 和 -XX:MaxPermSize 參數(shù)限制方法區(qū)的大小。

3. 運行時常量池

是方法區(qū)的一部分项玛,Class文件中除了有類的版本貌笨、字段、方法襟沮、接口等描述信息外锥惋,還有一項信息是常量池,用于存放編譯器生成的字面量和符號引用开伏,這部分內(nèi)容將在類加載后放到方法區(qū)的運行時常量池中膀跌。

存放基本類型常量和字符串常量。

4. 虛擬機棧

描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候 都會創(chuàng)建一個“棧幀”用于存儲局部變量表(包括參數(shù))固灵、操作棧捅伤、方法出口等信息。每個方法被調(diào)用到執(zhí)行完的過程巫玻,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程丛忆。聲明周期與線程相同祠汇,是線程私有的。

基礎數(shù)據(jù)類型直接在椣ü睿空間分配可很。

引用數(shù)據(jù)類型,需要用new來創(chuàng)建凰浮,既在椄睿空間分配一個地址空間,又在堆空間分配對象的類變量 导坟。

局部變量 new 出來時屿良,在棧空間和堆空間中分配空間惫周,當局部變量生命周期結(jié)束后尘惧,棧空間立刻被回收递递,堆空間區(qū)域等待GC回收喷橙。

數(shù)組既在棧空間分配數(shù)組名稱登舞, 又在堆空間分配數(shù)組實際的大小

方法的形式參數(shù)贰逾,直接在棧空間分配菠秒,當方法調(diào)用完成后從椄斫#空間回收。

方法的引用參數(shù)践叠,在椦早停空間分配一個地址空間,并指向堆空間的對象區(qū)禁灼,當方法調(diào)用完成后從椆苄空間回收。

5. 程序計數(shù)器

是最小的一塊內(nèi)存區(qū)域弄捕,它的作用是當前線程所執(zhí)行的字節(jié)碼的行號指示器僻孝,在虛擬機的模型里,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令守谓,分支穿铆、循環(huán)、異常處理分飞、線程恢復等基礎功能都需要依賴計數(shù)器完成悴务。

二、代碼示例存儲分配

JAVA的JVM的內(nèi)存可分為3個區(qū):堆(heap)、棧(stack)和方法區(qū)(method)

堆區(qū):

  1. 存儲的全部是對象讯檐,每個對象都包含一個與之對應的class的信息羡疗。(class的目的是得到操作指令)
  2. jvm只有一個堆區(qū)(heap)被所有線程共享,堆中不存放基本類型和對象引用别洪,只存放對象本身

棧區(qū):

  1. 每個線程包含一個棧區(qū)叨恨,棧中只保存基礎數(shù)據(jù)類型的對象和自定義對象的引用(不是對象),對象都存放在堆區(qū)中
  2. 每個棧中的數(shù)據(jù)(原始類型和對象引用)都是私有的挖垛,其他棧不能訪問
  3. 棧分為3個部分:基本類型變量區(qū)痒钝、執(zhí)行環(huán)境上下文、操作指令區(qū)(存放操作指令)

方法區(qū):

  1. 又叫靜態(tài)區(qū)痢毒,跟堆一樣送矩,被所有的線程共享。方法區(qū)包含所有的class和static變量
  2. 方法區(qū)中包含的都是在整個程序中永遠唯一的元素哪替,如class栋荸,static變量
//運行時, jvm把appmain的信息都放入方法區(qū)
public class AppMain {                  
    //main 方法本身放入方法區(qū)
    public static void main(String[] args) {  
        //test1是引用,所以放到棧區(qū)里凭舶, Sample是自定義對象應該放到堆里面
        Sample test1 = new Sample("測試1");       
        Sample test2 = new Sample("測試2");    
          
        test1.printName();    
        test2.printName();    
    }    
}

//運行時, jvm 把appmain的信息都放入方法區(qū)
public class Sample {
    /** 范例名稱 */
    // new Sample實例后晌块, name 引用放入棧區(qū)里, name 對象放入堆里
    private String name; 

    /** 構造方法 */
    public Sample(String name) {
        this.name = name;
    }

    /** 輸出 */
    // print方法本身放入 方法區(qū)里
    public void printName() { 
        System.out.println(name);
    }
}
行動向?qū)D

系統(tǒng)收到了我們發(fā)出的指令帅霜,啟動了一個Java虛擬機進程匆背,這個進程首先從classpath中找到AppMain.class文件,讀取這個文件中的二進制數(shù)據(jù)身冀,然后把Appmain類的類信息存放到運行時數(shù)據(jù)區(qū)的方法區(qū)中钝尸。這一過程稱為AppMain類的加載過程。

接著闽铐,Java虛擬機定位到方法區(qū)中AppMain類的Main()方法的字節(jié)碼蝶怔,開始執(zhí)行它的指令。這個main()方法的第一條語句就是:

Sample test1=new Sample("測試1"); 

語句很簡單啦兄墅,就是讓java虛擬機創(chuàng)建一個Sample實例,并且使引用變量test1引用這個實例澳叉。貌似小case一樁哦隙咸,就讓我們來跟蹤一下Java虛擬機,看看它究竟是怎么來執(zhí)行這個任務的:

  1. Java虛擬機一看成洗,不就是建立一個Sample實例嗎五督,簡單,于是就直奔方法區(qū)而去瓶殃,先找到Sample類的類型信息再說充包。結(jié)果并沒有找到,因為這會兒的方法區(qū)里還沒有Sample類(Sample類沒加載),于是Java虛擬機立馬加載了Sample類基矮,把Sample類的類型信息存放在方法區(qū)里淆储。

  2. Sample類加載好后,Java虛擬機做的第一件事情就是在堆區(qū)中為一個新的Sample實例分配內(nèi)存, 這個Sample實例持有著指向方法區(qū)的Sample類的類型信息的引用家浇。這里所說的引用本砰,實際上指的是Sample類的類型信息在方法區(qū)中的內(nèi)存地址,而這個地址呢钢悲,就存放了在Sample實例的數(shù)據(jù)區(qū)里点额。

  3. 在Java虛擬機進程中,每個線程都會擁有一個方法調(diào)用棧莺琳,用來跟蹤線程運行中一系列的方法調(diào)用過程还棱,棧中的每一個元素就被稱為棧幀,每當線程調(diào)用一個方法的時候就會向方法棧壓入一個新幀惭等。這里的幀用來存儲方法的參數(shù)珍手、局部變量和運算過程中的臨時數(shù)據(jù)。OK咕缎,原理講完了珠十,就讓我們來繼續(xù)我們的跟蹤行動!位于“=”前的test1是一個在main()方法中定義的變量凭豪,可見焙蹭,它是一個局部變量,因此嫂伞,它被會添加到了執(zhí)行main()方法的主線程的JAVA方法調(diào)用棧中孔厉。而“=”將把這個test1變量指向堆區(qū)中的Sample實例,也就是說帖努,它持有指向Sample實例的引用撰豺。

OK,到這里為止呢拼余,JAVA虛擬機就完成了這個簡單語句的執(zhí)行任務污桦。參考行動向?qū)D,我們終于初步摸清了JAVA虛擬機的一點點底細了匙监,COOL凡橱!

接下來,JAVA虛擬機將繼續(xù)執(zhí)行后續(xù)指令亭姥,在堆區(qū)里繼續(xù)創(chuàng)建另一個Sample實例稼钩,然后依次執(zhí)行它們的printName()方法。當Java虛擬機執(zhí)行test1.printName()方法時达罗,JAVA虛擬機根據(jù)局部變量test1持有的引用坝撑,定位到堆區(qū)中的Sample實例,再根據(jù)Sample實例持有的引用,定位到方法去中Sample類的類型信息巡李,從而獲得printName()方法的字節(jié)碼抚笔,接著執(zhí)行printName()方法包含的指令。

三击儡、Java虛擬機中堆和棧的區(qū)別

(1)棧(stack)與堆(heap)都是Java用來在Ram中存放數(shù)據(jù)的地方塔沃。與C++不同,Java自動管理棧和堆阳谍,程序員不能直接地設置椫瘢或堆。

(2)棧的優(yōu)勢是矫夯,存取速度比堆要快鸽疾,僅次于直接位于CPU中的寄存器。但缺點是训貌,存在棧中的數(shù)據(jù)大小與生存期必須是確定的制肮,缺乏靈活性。另外递沪,棧數(shù)據(jù)可以共享豺鼻,詳見第3點。堆的優(yōu)勢是可以動態(tài)地分配內(nèi)存大小款慨,生存期也不必事先告訴編譯器儒飒,Java的垃圾收集器會自動收走這些不再使用的數(shù)據(jù)。但缺點是檩奠,由于要在運行時動態(tài)分配內(nèi)存桩了,存取速度較慢。

(3)Java中的數(shù)據(jù)類型有兩種埠戳,一種是基本類型(primitive types), 共有8種井誉,即int, short, long, byte, float, double, boolean, char(注意,并沒有string的基本類型)整胃。這種類型的定義是通過諸如

int a = 3; 
long b = 255L;

的形式來定義的颗圣,稱為自動變量。值得注意的是屁使,自動變量存的是字面值欠啤,不是類的實例,即不是類的引用屋灌,這里并沒有類的存在。如int a = 3; 這里的a是一個指向int類型的引用应狱,指向3這個字面值共郭。這些字面值的數(shù)據(jù),由于大小可知,生存期可知(這些字面值固定定義在某個程序塊里面除嘹,程序塊退出后写半,字段值就消失了),出于追求速度的原因尉咕,就存在于棧中叠蝇。另外,棧有一個很重要的特殊性年缎,就是存在棧中的數(shù)據(jù)可以共享悔捶。假設我們同時定義

int a = 3;
int b = 3;

編譯器先處理int a = 3,首先它會在棧中創(chuàng)建一個變量為a的引用单芜,然后查找有沒有字面值為3的地址蜕该,沒找到,就開辟一個存放3這個字面值的地址洲鸠,然后將a指向3的地址堂淡。接著處理int b = 3;在創(chuàng)建完b的引用變量后扒腕,由于在棧中已經(jīng)有3這個字面值绢淀,便將b直接指向3的地址。這樣瘾腰,就出現(xiàn)了a與b同時均指向3的情況皆的。

特別注意的是,這種字面值的引用與類對象的引用不同居灯。假定兩個類對象的引用同時指向一個對象祭务,如果一個對象引用變量修改了這個對象的內(nèi)部狀態(tài),那么另一個對象引用變量也即刻反映出這個變化怪嫌。相反义锥,通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變的情況岩灭。如上例拌倍,我們定義完a與 b的值后,再令a=4噪径;那么窘拯,b不會等于4奋蔚,還是等于3。在編譯器內(nèi)部,遇到a=4舆乔;時,它就會重新搜索棧中是否有4的字面值祟牲,如果沒有,重新開辟地址存放4的值仑鸥;如果已經(jīng)有了,則直接將a指向這個地址变屁。因此a值的改變不會影響到b的值眼俊。

(4)棧是線程私有的,堆是線程共享的粟关。

四疮胖、Java代碼運行過程實例

有如下代碼:

代碼示例

(1)運行main方法前,Test類已經(jīng)加載到方法區(qū)了
(2)首先new一個Test對象闷板,內(nèi)存分配如圖所示

由于Visio軟件有問題了澎灸,往后在更新!

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛔垢,一起剝皮案震驚了整個濱河市击孩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鹏漆,老刑警劉巖巩梢,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異艺玲,居然都是意外死亡括蝠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門饭聚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忌警,“玉大人,你說我怎么就攤上這事秒梳》啵” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵酪碘,是天一觀的道長朋譬。 經(jīng)常有香客問我,道長兴垦,這世上最難降的妖魔是什么徙赢? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮探越,結(jié)果婚禮上狡赐,老公的妹妹穿的比我還像新娘。我一直安慰自己钦幔,他們只是感情好枕屉,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鲤氢,像睡著了一般搀庶。 火紅的嫁衣襯著肌膚如雪拐纱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天哥倔,我揣著相機與錄音,去河邊找鬼揍庄。 笑死咆蒿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的蚂子。 我是一名探鬼主播沃测,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼食茎!你這毒婦竟也來了蒂破?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤别渔,失蹤者是張志新(化名)和其女友劉穎附迷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哎媚,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡喇伯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拨与。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稻据。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖买喧,靈堂內(nèi)的尸體忽然破棺而出捻悯,到底是詐尸還是另有隱情,我是刑警寧澤淤毛,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布今缚,位于F島的核電站,受9級特大地震影響钱床,放射性物質(zhì)發(fā)生泄漏荚斯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一查牌、第九天 我趴在偏房一處隱蔽的房頂上張望事期。 院中可真熱鬧,春花似錦纸颜、人聲如沸兽泣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唠倦。三九已至称鳞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稠鼻,已是汗流浹背冈止。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留候齿,地道東北人熙暴。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像慌盯,于是被迫代替她去往敵國和親周霉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 《深入理解Java虛擬機》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分亚皂。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,097評論 1 34
  • 一俱箱、運行時數(shù)據(jù)區(qū)域 Java虛擬機管理的內(nèi)存包括幾個運行時數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機棧灭必、本地方法棧狞谱、堆、程序計數(shù)器厂财,...
    加油小杜閱讀 1,519評論 1 15
  • 1.1 概述 Java優(yōu)點: 1芋簿、結(jié)構嚴謹,面向?qū)ο?2璃饱、擺脫硬件平臺束縛与斤,實現(xiàn)了“一次編寫,到處運行”的理想; ...
    viciyforever閱讀 1,172評論 1 9
  • Java 虛擬機屏蔽了與具體操作系統(tǒng)平臺相關的信息,使得 Java 語言編譯程序只需生成在 Java 虛擬機上運行...
    尋夢的尕柳閱讀 863評論 0 11
  • 定義:確保一個類只有一個實列荚恶。單列模式有幾個要點:1撩穿、定義一個私有的構造函數(shù)2、一個私有的變量3谒撼、公開的靜態(tài)的獲取...
    耳_總閱讀 305評論 0 0