最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

深入理解JVM (1)(先發(fā)一節(jié), 其他的還在學(xué))

2023-04-21 05:04 作者:廢品批發(fā)  | 我要投稿

1 運(yùn)行時(shí)數(shù)據(jù)區(qū)

整體結(jié)構(gòu)

1.1 程序計(jì)數(shù)器(Program Counter Register)

它是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)先線程所執(zhí)行的字節(jié)碼的信號(hào)指示器;??

每一條JVM線程都有自己的PC寄存器,各條線程之間互不影響,獨(dú)立存儲(chǔ),這類內(nèi)存區(qū)域被稱為“線程私有”內(nèi)存;

在任意時(shí)刻,一條JVM線程只會(huì)執(zhí)行一個(gè)方法的代碼。該方法稱為該線程的當(dāng)前方法(Current Method)

如果該方法是java方法,那PC寄存器保存JVM正在執(zhí)行的字節(jié)碼指令的地址;

如果該方法是native,那PC寄存器的值是undefined;

此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒(méi)有規(guī)定任何OutOfMemoryError情況的區(qū)域;

1.2 Java虛擬機(jī)棧(Java Virtual Machine Stack)

與PC寄存器一樣,Java虛擬機(jī)棧也是線程私有的。每一個(gè)JVM線程都有自己的java虛擬機(jī)棧,這個(gè)棧與線程同時(shí)創(chuàng)建,它的生命周期與線程相同。

虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過(guò)程就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過(guò)程。

JVM stack 可以被實(shí)現(xiàn)成固定大小,也可以根據(jù)計(jì)算動(dòng)態(tài)擴(kuò)展。

如果采用固定大小的JVM stack設(shè)計(jì),那么每一條線程的JVM Stack容量應(yīng)該在線程創(chuàng)建時(shí)獨(dú)立地選定。JVM實(shí)現(xiàn)應(yīng)該提供調(diào)節(jié)JVM Stack初始容量的手段;如果采用動(dòng)態(tài)擴(kuò)展和收縮的JVM Stack方式,應(yīng)該提供調(diào)節(jié)最大、最小容量的手段。

如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度將拋出StackOverflowError;

如果JVM Stack可以動(dòng)態(tài)擴(kuò)展,但是在嘗試擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存時(shí)拋出OutOfMemoryError。

局部變量表中存儲(chǔ)基本類型和對(duì)象引用. ?

long和double在局部變量表需要占用兩個(gè)空間(spot), 其他類型占用一個(gè). ?

局部變量表的內(nèi)存空間在編譯時(shí)就已經(jīng)完全確定, 方法運(yùn)行期間變量表的大小不變.

1.3 本地方法棧(Native Method Stack)

本地方法棧與虛擬機(jī)棧作用相似,后者為虛擬機(jī)執(zhí)行Java方法服務(wù),而前者為虛擬機(jī)用到的Native方法服務(wù)。

虛擬機(jī)規(guī)范對(duì)于本地方法棧中方法使用的語(yǔ)言,使用方式和數(shù)據(jù)結(jié)構(gòu)沒(méi)有強(qiáng)制規(guī)定,甚至有的虛擬機(jī)(比如HotSpot)直接把二者合二為一。

這玩意兒拋出的異常跟上面的虛擬機(jī)棧一樣。

1.4 Java堆(Java Heap)

虛擬機(jī)管理的內(nèi)存中最大的一塊,同時(shí)也是被所有線程所共享的,它在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,這貨存在的意義就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例以及數(shù)組都要在這里分配內(nèi)存。這里面的對(duì)象被自動(dòng)管理,也就是俗稱的GC(Garbage Collector)所管理。用就是了,有GC扛著呢,不用操心銷毀回收的事兒。

?Java堆的容量可以是固定大小,也可以隨著需求動(dòng)態(tài)擴(kuò)展(-Xms和-Xmx),并在不需要過(guò)多空間時(shí)自動(dòng)收縮。

Java堆所使用的內(nèi)存不需要保證是物理連續(xù)的,只要邏輯上是連續(xù)的即可。

JVM實(shí)現(xiàn)應(yīng)當(dāng)提供給程序員調(diào)節(jié)Java堆初始容量的手段,對(duì)于可動(dòng)態(tài)擴(kuò)展和收縮的堆來(lái)說(shuō),則應(yīng)當(dāng)提供調(diào)節(jié)其最大和最小容量的手段。

如果堆中沒(méi)有內(nèi)存完成實(shí)例分配并且堆也無(wú)法擴(kuò)展,就會(huì)拋OutOfMemoryError。??

java虛擬機(jī)堆的內(nèi)在結(jié)構(gòu)存在更加復(fù)雜的劃分
其中Eden, from, to都屬于年輕代(新生代).


1.5 方法區(qū)(Method Area)(1.8之前)

跟堆一樣是被各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)以被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。雖然這個(gè)區(qū)域被虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它的別名叫非堆,用來(lái)與堆做一下區(qū)別。

方法區(qū)在虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建。

方法區(qū)的容量可以是固定大小的,也可以隨著程序執(zhí)行的需求動(dòng)態(tài)擴(kuò)展,并在不需要過(guò)多空間時(shí)自動(dòng)收縮。

方法區(qū)在實(shí)際內(nèi)存空間中可以是不連續(xù)的。

Java虛擬機(jī)實(shí)現(xiàn)應(yīng)當(dāng)提供給程序員或者最終用戶調(diào)節(jié)方法區(qū)初始容量的手段,對(duì)于可以動(dòng)態(tài)擴(kuò)展和收縮方法區(qū)來(lái)說(shuō),則應(yīng)當(dāng)提供調(diào)節(jié)其最大、最小容量的手段。

當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí)就會(huì)拋OutOfMemoryError。


下面有一個(gè)小例子,來(lái)說(shuō)明堆,棧和方法區(qū)之間的關(guān)系的.

? ? public class Test2 {

? ? ? ? public static void main(String[] args) {

? ? ? ? ? ? public Test2 t2 = new Test2();

? ? ? ? ? ? //JVM將Test2類信息加載到方法區(qū),new Test2()實(shí)例保存在堆區(qū),t2引用保存在棧區(qū) ?

? ? ? ? }

? ? }??

1.6 元空間

方法區(qū)是一種抽象的定義, 1.8后hotspot使用“元空間”來(lái)實(shí)現(xiàn)方法區(qū), 方法區(qū)位于本地內(nèi)存之中, 只要本地內(nèi)存還有剩余, 理論上就可以擴(kuò)展, 而不會(huì)受制于JVM的內(nèi)存限制.


1.7 運(yùn)行時(shí)常量池(Runtime Constant Pool), 注意與class文件常量池區(qū)分

它是方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項(xiàng)信息是常量池(Constant Pool Table),用于存放編譯期生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中。

Java虛擬機(jī)對(duì)Class文件的每一部分(自然也包括常量池)的格式都有嚴(yán)格的規(guī)定,每一個(gè)字節(jié)用于存儲(chǔ)哪種數(shù)據(jù)都必須符合規(guī)范上的要求,這樣才會(huì)被虛擬機(jī)認(rèn)可、裝載和執(zhí)行。但對(duì)于運(yùn)行時(shí)常量池,Java虛擬機(jī)規(guī)范沒(méi)有做任何細(xì)節(jié)的要求,不同的提供商實(shí)現(xiàn)的虛擬機(jī)可以按照自己的需要來(lái)實(shí)現(xiàn)這個(gè)內(nèi)存區(qū)域。不過(guò),一般來(lái)說(shuō),除了保存Class文件中描述的符號(hào)引用外,還會(huì)把翻譯出來(lái)的直接引用也存儲(chǔ)在運(yùn)行時(shí)常量池中。

運(yùn)行時(shí)常量池相對(duì)于Class文件常量池的另外一個(gè)重要特征是具備動(dòng)態(tài)性,Java語(yǔ)言并不要求常量一定只能在編譯期產(chǎn)生,也就是并非預(yù)置入Class文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時(shí)常量池,運(yùn)行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用得比較多的便是String類的intern()方法。

既然運(yùn)行時(shí)常量池是方法區(qū)的一部分,自然會(huì)受到方法區(qū)內(nèi)存的限制,當(dāng)常量池?zé)o法再申請(qǐng)到內(nèi)存時(shí)會(huì)拋出OutOfMemoryError異常。

????????關(guān)于class文件常量池,是class文件中記錄的字面量和符號(hào)引用,class文件還包含類的版本、字段、方法、接口等描述信息。class文件常量池將在類的加載之后到達(dá)方法區(qū)的運(yùn)行時(shí)常量池,在運(yùn)行時(shí)常量池中符號(hào)引用將轉(zhuǎn)化為直接引用。

1.8 直接內(nèi)存(Direct Memory)

直接內(nèi)存(Direct Memory)并不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域,但是這部分內(nèi)存也被頻繁地使用,而且也可能導(dǎo)致OutOfMemoryError異常出現(xiàn)。

JDK1.4加的NIO中,ByteBuffer有個(gè)方法是allocateDirect(int capacity) ,這是一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O方式,它可以使用Native函數(shù)庫(kù)直接分配堆外內(nèi)存,然后通過(guò)一個(gè)存儲(chǔ)在Java堆里面的DirectByteBuffer對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作。這樣能在一些場(chǎng)景中顯著提高性能,因?yàn)楸苊饬嗽贘ava堆和Native堆中來(lái)回復(fù)制數(shù)據(jù)。

顯然,本機(jī)直接內(nèi)存的分配不會(huì)受到Java堆大小的限制,但是,既然是內(nèi)存,則肯定還是會(huì)受到本機(jī)總內(nèi)存(包括RAM及SWAP區(qū)或者分頁(yè)文件)的大小及處理器尋址空間的限制。服務(wù)器管理員配置虛擬機(jī)參數(shù)時(shí),一般會(huì)根據(jù)實(shí)際內(nèi)存設(shè)置-Xmx等參數(shù)信息,但經(jīng)常會(huì)忽略掉直接內(nèi)存,使得各個(gè)內(nèi)存區(qū)域的總和大于物理內(nèi)存限制(包括物理上的和操作系統(tǒng)級(jí)的限制),從而導(dǎo)致動(dòng)態(tài)擴(kuò)展時(shí)出現(xiàn)OutOfMemoryError異常。

1.9 代碼運(yùn)行中的java虛擬機(jī)棧

我們使用javap -c(反編譯)或-v(反編譯附加詳細(xì)信息)命令來(lái)查看字節(jié)碼.


javap命令與java代碼


i++與++i的區(qū)別:

i++, ++i指令碼與java代碼


通過(guò)棧中的數(shù)據(jù)變動(dòng)的角度去看, i++與++i, 當(dāng)我們單獨(dú)使用這兩句時(shí),

? ? int i=0;

? ? i++;

或者

? ? int i=0;

? ? ++i;

此時(shí)的編譯字節(jié)碼指令一模一樣,

? ? iconst_1 ?1進(jìn)入操作數(shù)棧

? ? istore_1 ?棧中數(shù)據(jù)彈出, 存入局部變量表位置1

? ? //上面兩句是解釋 int i=0

? ? iinc 1,1 變量表1位置加1

iinc 1,1 該命令可不是計(jì)算1+1, 而是局部變量表的1位置加上數(shù)字1.


當(dāng)我們把i++進(jìn)行賦值時(shí),如上圖黃色和藍(lán)色區(qū)域中, 進(jìn)行的操作是:

? ? 1. 變量表中數(shù)據(jù)加載到棧

? ? 2. 變量表中數(shù)據(jù)自增1

? ? 3. 棧中數(shù)據(jù)存回變量表

自增之前就已經(jīng)加載到棧, 自增的是變量表中的數(shù), 棧中已經(jīng)加載的沒(méi)有隨之改變, 存回變量表的數(shù)據(jù)是沒(méi)有自增的.

而圖中紫色部分:

? ? int k=i + ++i*i++;

解析指令:

? ?iload_1 ? ? ? ?//代碼第一個(gè)i, 變量表中的數(shù)據(jù)入棧

? ? iinc 1,1 ? ? ? //代碼++i

? ? iload_1

? ?

? ? iload_1 ? ? ? ?//代碼i++

? ? iinc 1,1


? ? imul

? ? iadd

? ? istore_3

可見(jiàn), 帶賦值時(shí) i++與++i 前者是先加載數(shù)據(jù)到棧再自增,后者是是先自增再加載, 只要牢記自增指令iinc是變量表中自增就容易理解了.

總結(jié)結(jié)構(gòu)組成


題外話: ?

????A a=new A(); ?

????對(duì)于這樣的代碼, javap -v后,??

? ????????new ?#?

? ????????dup

????????? invokespecial #?

????????? astore_X

new后面緊跟著的#?, 可以在常量池中定位Class, 這一句指令直接就會(huì)創(chuàng)建對(duì)象, 當(dāng)然在此之前要加載這個(gè)類, 實(shí)例化對(duì)象(此時(shí)還未初始化對(duì)象),對(duì)象的引用已經(jīng)被放在操作數(shù)棧中. ?

dup命令, 把操作數(shù)棧中的引用復(fù)制兩份, ?

原因是下一句指令invokespecial調(diào)用構(gòu)造方法, 構(gòu)造方法沒(méi)有返回值, 但卻一定需要消耗操作數(shù)棧中的對(duì)象引用 (即使是無(wú)參構(gòu)造,該構(gòu)造方法也會(huì)有this這樣一個(gè)符號(hào)引用) , ?

如果不把操作數(shù)棧中的引用額外復(fù)制一份, 一旦它被消耗, 我們就丟失了該對(duì)象的訪問(wèn), 復(fù)制一次, 兩個(gè)引用都指向同一個(gè)對(duì)象, 在初始化后, 我們還有一個(gè)引用可以執(zhí)行astore, 存入局部變量表. ?

**類的加載后, 進(jìn)行類的實(shí)例化, 然后進(jìn)行類的初始化** ?

我認(rèn)為new a();這一句代碼需要分為兩部分, new本身就是創(chuàng)建對(duì)象, 后面的a(), 是初始化該對(duì)象.

深入理解JVM (1)(先發(fā)一節(jié), 其他的還在學(xué))的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
黄冈市| 定襄县| 布拖县| 繁峙县| 中超| 峨眉山市| 海口市| 密山市| 高邑县| 庆阳市| 金乡县| 安化县| 枣强县| 夏河县| 衡阳县| 保康县| 文山县| 马山县| 彰化市| 赤峰市| 漠河县| 迁安市| 沅陵县| 达日县| 新营市| 丰顺县| 湖南省| 靖边县| 镇康县| 新昌县| 札达县| 泌阳县| 六枝特区| 大姚县| 五大连池市| 保靖县| 安龙县| 嵊州市| 榆林市| 璧山县| 沂水县|