自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官問(wèn):Java中的new關(guān)鍵字做了什么事情?

開(kāi)發(fā) 前端
1. 64位開(kāi)啟指針壓縮的情況下,存放Class指針的空間大小是4字節(jié),MarkWord是8字節(jié),對(duì)象頭為12字節(jié)。數(shù)組長(zhǎng)度4字節(jié)+數(shù)組對(duì)象頭8字節(jié)(對(duì)象引用4字節(jié)(未開(kāi)啟指針壓縮的64位為8字節(jié))+數(shù)組markword為4字節(jié)(64位未開(kāi)啟指針壓縮的為8字節(jié)))+對(duì)齊4=16字節(jié)。

各位小伙伴,當(dāng)我們new一個(gè)對(duì)象的時(shí)候,對(duì)象到底是怎么生產(chǎn)出來(lái)的呢,我們這篇說(shuō)一說(shuō)對(duì)象生成的過(guò)程和內(nèi)存的分配機(jī)制,面試的時(shí)候可以扯一扯,絕對(duì)是加分項(xiàng)。

圖片圖片

1.加載類時(shí)檢查

虛擬機(jī)在執(zhí)行的過(guò)程中,執(zhí)行到new關(guān)鍵字(new關(guān)鍵詞、對(duì)象克隆、對(duì)象序列化等)的時(shí)候,第一步是先去檢查這個(gè)指令的參數(shù)對(duì)應(yīng)的符號(hào)引用是否在常量池中,其對(duì)應(yīng)的類是否已經(jīng)被加載解析和初始化,如果已經(jīng)有,就代表此類已經(jīng)被加載過(guò)了,如果嗎,沒(méi)有就說(shuō)明類還沒(méi)有被加載,那就要執(zhí)行類記載的整個(gè)過(guò)程。

2.內(nèi)存的分配

在類加載過(guò)程完成后,就要對(duì)新創(chuàng)建的對(duì)象進(jìn)行分配內(nèi)存的操作,那么對(duì)應(yīng)所需要的內(nèi)存具體大小是如何確定的呢,其實(shí)對(duì)象所需內(nèi)存的大小在類加載完成后就可以完全確定了,虛擬機(jī)只需要在java堆中劃分出相應(yīng)大小的固定的一塊內(nèi)存空間即可。

但是在分配內(nèi)存這個(gè)過(guò)程中有兩個(gè)問(wèn)題:

  • 如何劃分內(nèi)存。
  • 在并發(fā)情況下, 可能出現(xiàn)正在給對(duì)象A分配內(nèi)存,指針還沒(méi)來(lái)得及修改,對(duì)象B又同時(shí)使用了原來(lái)的指針來(lái)分配內(nèi)存的情況。虛擬機(jī)有兩種內(nèi)存分配方法,一種是“指針碰撞”,一種是“空閑列表”,java默認(rèn)采用的是指針碰撞,指針碰撞針對(duì)于規(guī)整的java堆,被使用的內(nèi)存全都集中在堆的一邊,而另一邊都是空閑的內(nèi)存,當(dāng)需要分配固定大小的內(nèi)存時(shí)候,只需要將內(nèi)存的指針(分界點(diǎn)的指示器)從當(dāng)前使用的位置向后挪動(dòng)相應(yīng)大小即可。當(dāng)堆內(nèi)存分配不是規(guī)整的時(shí)候,被使用的內(nèi)存和沒(méi)有被使用的內(nèi)存交錯(cuò)相間,虛擬機(jī)很難找到一塊固定大小且連續(xù)的內(nèi)存空間,這時(shí)候指針碰撞就很難發(fā)揮出作用,這個(gè)時(shí)候虛擬機(jī)采用的是空閑列表,空閑列表是用來(lái)維護(hù)哪些內(nèi)存塊是空閑的,在進(jìn)行分配內(nèi)存的時(shí)候,只需要去空閑列表中找到一塊大小合適且連續(xù)的內(nèi)存塊就可以了,然后再把這塊內(nèi)存空間在空閑列表上更新其記錄。

解決并發(fā)問(wèn)題的方法:

CAS(compare and swap): 虛擬機(jī)采用CAS配上失敗重試的方式保證更新操作的原子性來(lái)對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理。

本地線程分配緩沖(Thread Local Allocation Buffer,TLAB): 把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間之中進(jìn)行,即每個(gè)線程在Java堆中預(yù)先分配一小塊內(nèi)存。

通過(guò)-XX:+/-UseTLAB參數(shù)來(lái)設(shè)定虛擬機(jī)是否使用TLAB(JVM會(huì)默認(rèn)開(kāi)啟-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。

3.初始化零值

內(nèi)存分配完成后,虛擬機(jī)需要將分配到的內(nèi)存空間都初始化為零值(不包括對(duì)象頭), 如果使用TLAB,這一工作過(guò)程也可以提前至TLAB分配時(shí)進(jìn)行。這一步操作保證了對(duì)象的實(shí)例字段在Java代碼中可以不賦初始值就直接使用,程序能訪問(wèn)到這些字段的數(shù)據(jù)類型所對(duì)應(yīng)的零值。

4.設(shè)置對(duì)象頭

初始化零值之后,虛擬機(jī)要對(duì)對(duì)象進(jìn)行必要的設(shè)置,例如這個(gè)對(duì)象是哪個(gè)類的實(shí)例、如何才能找到類的元數(shù)據(jù)信息、對(duì)象的哈希碼、對(duì)象的GC分代年齡等信息。這些信息存放在對(duì)象的對(duì)象頭Object Header之中。

在HotSpot虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對(duì)象頭(Header)、 實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding)。HotSpot虛擬機(jī)的對(duì)象頭包括兩部分信息,第一部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù), 如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID、偏向時(shí) 間戳等。對(duì)象頭的另外一部分是類型指針,即對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針來(lái)確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。

32位對(duì)象頭:

圖片圖片

64位對(duì)象頭:

圖片圖片

5.執(zhí)行方法

執(zhí)行方法,即對(duì)象按照程序員的意愿進(jìn)行初始化。對(duì)應(yīng)到語(yǔ)言層面上講,就是為屬性賦值(注意,這與上面的賦零值不同,這是由程序員賦的值),和執(zhí)行構(gòu)造方法。

對(duì)象大小與指針壓縮

對(duì)象大小可以用jol-core包查看,引入依賴

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
import org.openjdk.jol.info.ClassLayout;

/**
 * 計(jì)算對(duì)象大小
 */
public class JOLSample {

    public static void main(String[] args) {
        ClassLayout layout = ClassLayout.parseInstance(new Object());
        System.out.println(layout.toPrintable());

        System.out.println();
        ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
        System.out.println(layout1.toPrintable());

        System.out.println();
        ClassLayout layout2 = ClassLayout.parseInstance(new A());
        System.out.println(layout2.toPrintable());
    }

    // -XX:+UseCompressedOops           默認(rèn)開(kāi)啟的壓縮所有指針
    // -XX:+UseCompressedClassPointers  默認(rèn)開(kāi)啟的壓縮對(duì)象頭里的類型指針Klass Pointer
    // Oops : Ordinary Object Pointers
    public static class A {
                       //8B mark word
                       //4B Klass Pointer   如果關(guān)閉壓縮-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,則占用8B
        int id;        //4B
        String name;   //4B  如果關(guān)閉壓縮-XX:-UseCompressedOops,則占用8B
        byte b;        //1B 
        Object o;      //4B  如果關(guān)閉壓縮-XX:-UseCompressedOops,則占用8B
    }
}


運(yùn)行結(jié)果:
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)    //mark word
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)    //mark word     
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)    //Klass Pointer
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


[I object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     0    int [I.<elements>                             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total


com.tuling.jvm.JOLSample$A object internals:
 OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
      0     4                    (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                    (object header)                           61 cc 00 f8 (01100001 11001100 00000000 11111000) (-134165407)
     12     4                int A.id                                      0
     16     1               byte A.b                                       0
     17     3                    (alignment/padding gap)                  
     20     4   java.lang.String A.name                                    null
     24     4   java.lang.Object A.o                                       null
     28     4                    (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

什么是java對(duì)象的指針壓縮?

1.jdk1.6 update14開(kāi)始,在64bit操作系統(tǒng)中,JVM支持指針壓縮

2.jvm配置參數(shù):UseCompressedOops,compressed--壓縮、oop(ordinary object pointer)--對(duì)象指針

3.啟用指針壓縮:-XX:+UseCompressedOops(默認(rèn)開(kāi)啟),禁止指針壓縮:-XX:-UseCompressedOops

為什么要進(jìn)行指針壓縮?

1.在64位平臺(tái)的HotSpot中使用32位指針(實(shí)際存儲(chǔ)用64位),內(nèi)存使用會(huì)多出1.5倍左右,使用較大指針在主內(nèi)存和緩存之間移動(dòng)數(shù)據(jù),占用較大寬帶,同時(shí)GC也會(huì)承受較大壓力2.為了減少64位平臺(tái)下內(nèi)存的消耗,啟用指針壓縮功能

3.在jvm中,32位地址最大支持4G內(nèi)存(2的32次方),可以通過(guò)對(duì)對(duì)象指針的存入堆內(nèi)存時(shí)壓縮編碼、取出到cpu寄存器后解碼方式進(jìn)行優(yōu)化(對(duì)象指針在堆中是32位,在寄存器中是35位,2的35次方=32G),使得jvm只用32位地址就可以支持更大的內(nèi)存配置(小于等于32G)

4.堆內(nèi)存小于4G時(shí),不需要啟用指針壓縮,jvm會(huì)直接去除高32位地址,即使用低虛擬地址空間

5.堆內(nèi)存大于32G時(shí),壓縮指針會(huì)失效,會(huì)強(qiáng)制使用64位(即8字節(jié))來(lái)對(duì)java對(duì)象尋址,這就會(huì)出現(xiàn)1的問(wèn)題,所以堆內(nèi)存不要大于32G為好

對(duì)象大小計(jì)算

1. 在32位系統(tǒng)下,存放Class指針的空間大小是4字節(jié),MarkWord是4字節(jié),對(duì)象頭為8字節(jié)。

2. 在64位系統(tǒng)下,存放Class指針的空間大小是8字節(jié),MarkWord是8字節(jié),對(duì)象頭為16字節(jié)。

3. 64位開(kāi)啟指針壓縮的情況下,存放Class指針的空間大小是4字節(jié),MarkWord是8字節(jié),對(duì)象頭為12字節(jié)。數(shù)組長(zhǎng)度4字節(jié)+數(shù)組對(duì)象頭8字節(jié)(對(duì)象引用4字節(jié)(未開(kāi)啟指針壓縮的64位為8字節(jié))+數(shù)組markword為4字節(jié)(64位未開(kāi)啟指針壓縮的為8字節(jié)))+對(duì)齊4=16字節(jié)。

4. 靜態(tài)屬性不算在對(duì)象大小內(nèi)。

關(guān)于對(duì)齊填充:對(duì)于大部分處理器,對(duì)象以8字節(jié)整數(shù)倍來(lái)對(duì)齊填充都是最高效的存取方式。

責(zé)任編輯:武曉燕 來(lái)源: 碼農(nóng)本農(nóng)
相關(guān)推薦

2018-01-19 10:43:06

Java面試官volatile關(guān)鍵字

2010-03-08 08:39:54

類加載器newJava

2022-06-09 11:20:44

volatile關(guān)鍵字

2020-06-08 15:15:26

程序員擺地?cái)?/a>技術(shù)

2024-06-27 16:27:04

transientJava序列化

2024-03-13 07:53:57

弱引用線程工具

2022-01-05 09:55:26

asynawait前端

2021-11-08 09:18:01

CAS面試場(chǎng)景

2021-12-25 22:31:10

MarkWord面試synchronize

2009-08-21 14:16:35

C# New關(guān)鍵字

2023-07-03 08:17:44

JUC工具代碼

2021-12-16 18:38:13

面試Synchronize

2012-06-02 00:53:39

Javafinally

2020-08-10 08:00:13

JavaFinal關(guān)鍵字

2021-09-07 10:44:33

Java 注解開(kāi)發(fā)

2023-06-05 07:57:53

Kafka消息事務(wù)消息

2023-03-24 08:01:27

Go語(yǔ)言內(nèi)存

2021-12-02 18:20:25

算法垃圾回收

2024-01-11 08:12:20

重量級(jí)監(jiān)視器

2020-07-28 00:58:20

IP地址子網(wǎng)TCP
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)