做包子網(wǎng)站營銷軟件哪個好
1、JVM棧的數(shù)據(jù)存儲
??????? 通過前面的學習,我們知道,將源代碼編譯成字節(jié)碼文件后,JVM會對其中的字節(jié)碼指令解釋執(zhí)行,在解釋執(zhí)行的過程中,又利用到了棧區(qū)的操作數(shù)棧和局部變量表兩部分。
??????? 而局部變量表又分為一個個的槽位,通常第0個槽位存放實例方法的this。而每個槽位的空間大小是根據(jù)不同虛擬機決定的:
- 32位虛擬機:32位,4個字節(jié)。
- 64位虛擬機:64位,8個字節(jié)。
??????? 在Java中有八大基本數(shù)據(jù)類型,它們在堆中占用的字節(jié)數(shù)都是不同的,如下表所示:
??????? 其中內(nèi)存占用最多的是long和double類型,8個字節(jié)。
??????? 它們在局部變量表中,需要占用兩個槽位。如果這樣說,很多人會有疑問,"每個槽位的空間大小是根據(jù)不同虛擬機決定的",如果是32位虛擬機,每個槽位只有4個字節(jié),占用兩個槽位是沒有問題的。但是如果是64位虛擬機呢?每個槽位有8個字節(jié),按理說一個槽位就可以放下了,為什么還是需要占用兩個槽位?
??????? 原因在于,Java語言的跨平臺特性。局部變量表是在編譯期間就確定下來的,無法得知將來JVM會在何種環(huán)境下解釋執(zhí)行字節(jié)碼指令。所以為了保證通用性,統(tǒng)一按照64位虛擬機進行考慮。并且雖然long和double類型占用了兩個槽位共16個字節(jié),實際上它的高8字節(jié)是沒有被使用的。
??????? 棧中的數(shù)據(jù)要保存到堆上&堆中加載到棧上需要如何實現(xiàn)?
??????? 在編譯成的字節(jié)碼文件中,所有占用了一個槽位的數(shù)據(jù)類型都是被當做了int執(zhí)行(iconst),int默認占有4個字節(jié),也就是在棧上這些都默認占有了4個字節(jié)。
??????? 但是在堆上,它們實際有的只占有了1個字節(jié),有的占有了2個字節(jié)。所以棧中的數(shù)據(jù)要保存在堆上,需要進行截取,堆中的數(shù)據(jù)加載到棧上則反之。
???????? 在進行過程分析之前,首先要明白兩個概念,符號位和高(低)位
- 符號位:符號位是在計算機中用來表示數(shù)值的正負的特殊位。通常情況下,一個整數(shù)的符號位是用來表示該整數(shù)是正數(shù)還是負數(shù)的。在計算機中,符號位通常是由整數(shù)類型的最高位(最左側的位)來表示的,其中 0 表示正數(shù),1 表示負數(shù)。例如,在一個有符號的 8 位整數(shù)中,如果最高位是 0,則該整數(shù)被解釋為正數(shù);如果最高位是 1,則該整數(shù)被解釋為負數(shù)。
- 高低位:通常是指在計算機中用于表示數(shù)字的二進制位的位置。在一個多字節(jié)的數(shù)據(jù)類型(比如整數(shù)或者浮點數(shù))中,位被分為高位和低位兩部分。高位是數(shù)據(jù)中權值最高的位,通常位于數(shù)據(jù)的最左側。在有符號整數(shù)中,高位通常用于表示符號位(0 表示正數(shù),1 表示負數(shù))。在無符號整數(shù)中,高位用于表示最大的數(shù)值。低位是數(shù)據(jù)中權值最低的位,通常位于數(shù)據(jù)的最右側。低位存儲著數(shù)據(jù)的最小的權值。
????????堆中的數(shù)據(jù)加載到棧上時,也要考慮符號位的問題。boolean和char類型沒有符號位,直接高位補0即可,byte和short有符號位,低位直接復制,高位負則補1,非負補0:
??????? 棧中的數(shù)據(jù)要保存在堆上 ,需要將高位截取掉。而boolean類型只取低位的最后一位。
2、JVM對象在堆上的存儲
???????? 對象在堆中的內(nèi)存布局,分為對象頭和對象數(shù)據(jù)兩部分,對象頭中又有標記字和類型指針。如果是數(shù)組對象類型,對象頭中額外保存了數(shù)組的容量。
??????? 其中標記字 的結構,在64位和32位虛擬機中都不一樣,其中64位虛擬機又分為是否進行了指針壓縮:
這一塊詳見:http://t.csdnimg.cn/8D7DI
??????? 我們也可以通過JoL jar包中提供的方法打印出這一塊的信息:
??????? 在打印出的信息中,我們可以發(fā)現(xiàn),引用數(shù)據(jù)類型是排在最后的,并且在類的定義中,是long變量在前,int變量在后。但是打印出的對象頭信息中,兩者的順序發(fā)生了交換,原因在于,每個屬性的偏移量必須是字段長度的整數(shù)倍。在這個案例中,假如long變量在前,那么它的OFFSET和SIZE 就會變成12和8,不滿足整數(shù)倍的條件,所以會做出調(diào)整。這就是內(nèi)存對齊。
??????? 而內(nèi)存對齊的主要目的是為了解決在并發(fā)環(huán)境下cpu緩存失效的情況。
??????? 簡單來說,在一個緩存行中,可能存在多個實例的緩存。當其中一個實例的緩存失效需要更新時,會讓整個緩存行都失效。從而影響到緩存行中的其他對象。
??????? 內(nèi)存對齊后,可以理解成同一個緩存行不會存有不同類型的對象,即使某個對象的緩存失效,也不會影響其他的對象:
??????? 上面也提到過指針壓縮的概念。什么是指針壓縮?
????????指針壓縮是一種優(yōu)化技術,用于減小程序中指針所占用的內(nèi)存空間。
????????在64位系統(tǒng)中,一個指針通常需要8個字節(jié)。但在許多情況下,指針實際指向的地址并不需要使用那么多的位來表示,因為程序的內(nèi)存地址空間可能不會達到8字節(jié)指針所能表示的范圍。
????????指針壓縮技術利用這一點,通過降低指針所占用的位數(shù)來節(jié)省內(nèi)存空間。這通常是通過將指針存儲為較小的數(shù)據(jù)類型(比如32位系統(tǒng)中的4字節(jié))來實現(xiàn)的,因為在大多數(shù)情況下,程序的內(nèi)存地址空間并不會達到需要用到更多位表示的程度。
????????指針壓縮的一個常見實現(xiàn)方式是使用對象偏移量(Object Offset)來表示指針。在這種情況下,指針不再直接存儲對象的內(nèi)存地址,而是存儲對象相對于某個基準地址(如堆的起始地址)的偏移量。通過這種方式,可以使用較少的位數(shù)來表示指針,從而節(jié)省內(nèi)存空間。
??????? 例如下圖,左邊的部分沒有進行指針壓縮,右邊的部分進行了指針壓縮。
??????? 沒有進行指針壓縮時,當前對象的內(nèi)存地址是8,并且占用了8個字節(jié)。進行了內(nèi)存壓縮后,指針中不存放真實的地址,而是存放編號(偏移量)。
?