成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解Java虛擬機(jī):對(duì)象實(shí)例化及直接內(nèi)存詳解

開發(fā) 前端
在Java世界中,萬物皆對(duì)象。當(dāng)我們談?wù)撘粋€(gè)對(duì)象時(shí),其實(shí)質(zhì)是指代一段具有特定屬性和行為的內(nèi)存區(qū)域。

前言

在Java世界中,萬物皆對(duì)象。當(dāng)我們談?wù)撘粋€(gè)對(duì)象時(shí),其實(shí)質(zhì)是指代一段具有特定屬性和行為的內(nèi)存區(qū)域。在JVM的視角下,對(duì)象的存儲(chǔ)并非簡(jiǎn)單的字節(jié)序列堆積,而是遵循著嚴(yán)謹(jǐn)?shù)慕Y(jié)構(gòu)設(shè)計(jì)與管理規(guī)則。從創(chuàng)建到消亡,一個(gè)Java對(duì)象在JVM中的生命歷程主要圍繞堆內(nèi)存展開,而堆正是JVM內(nèi)存模型中用于存儲(chǔ)對(duì)象實(shí)例的主要區(qū)域。

本文將圍繞對(duì)象的實(shí)例化、對(duì)象內(nèi)存布局、對(duì)象的訪問定位和直接內(nèi)存展開介紹說明。

對(duì)象實(shí)例化

圖片圖片

創(chuàng)建對(duì)象的方式

最常見的方式new、Xxx的靜態(tài)方法,XxxBuilder/XxxFactory的靜態(tài)方法:

Student student = new Student();

Class的newInstance方法:反射的方式,只能調(diào)用空參的構(gòu)造器,權(quán)限必須是public:

Class clazz = Class.forName("org.yian.Student");
Student student = (Student) clazz.newInstance();

Constructor的newInstance(XXX):反射的方式,可以調(diào)用空參、帶參的構(gòu)造器,權(quán)限沒有要求:

Student.class.getConstructor().newInstance();

使用clone():不調(diào)用任何的構(gòu)造器,要求當(dāng)前的類需要實(shí)現(xiàn)Cloneable接口,實(shí)現(xiàn)clone():

Student clone = student.clone();

使用序列化:從文件中、從網(wǎng)絡(luò)中獲取一個(gè)對(duì)象的二進(jìn)制流:

public class Student implements Serializable {
    private String name;
    private Integer age;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Integer getAge() {
        return age;
    }
 
    public void setAge(Integer age) {
        this.age = age;
    }
 
    public void say() {
        System.out.println("hello i'm yian!"+this.age);
    }
 
 
    public static void main(String[] args) throws Exception {
 
        Student student = new Student();
        student.setAge(10);
        String filePath = "com";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(student);
        oos.close();
        System.out.println("序列化完成!");
 
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        Student student1 = (Student) ois.readObject();
        ois.close();
        student1.say();
        System.out.println("反序列化完成!");
 
    }
}

第三方庫 Objenesis:

Objenesis objenesis = new ObjenesisStd();
ObjectInstantiator<Student> instantiator = objenesis.getInstantiatorOf(Student.class);
Student st = instantiator.newInstance();

創(chuàng)建對(duì)象的步驟

字節(jié)碼分析對(duì)象創(chuàng)建

javap -v -p Student.class:

圖片圖片

  • new:會(huì)首先檢查這個(gè)Class有沒有加載,即加載、鏈接、初始化?并按照編譯中的大小信息分配空間,進(jìn)行創(chuàng)建對(duì)象 ,并對(duì)其臨時(shí)初始化
  • dup:第一句new會(huì)在操作數(shù)棧中生成一個(gè)指向該對(duì)象的引用,dup指令會(huì)將這個(gè)引用再復(fù)制一遍,放到操作數(shù)棧的棧頂,上面那個(gè)引用是作為一個(gè)句柄指向方法區(qū)的對(duì)應(yīng)方法。
  • invokespecial :進(jìn)行真實(shí)初始化,為其賦實(shí)際的初始值【即調(diào)用構(gòu)造器方法<init>】

注意:

<init>與<clinit>的區(qū)別:前者是一個(gè)類的構(gòu)造器在字節(jié)碼中對(duì)應(yīng)的方法,后者習(xí)慣被稱為類構(gòu)造器方法,他會(huì)在類加載的初始化階段對(duì)類的靜態(tài)部分進(jìn)行初始化【如靜態(tài)代碼塊,靜態(tài)成員變量等】

JVM創(chuàng)建對(duì)象的步驟

1.判斷對(duì)象對(duì)應(yīng)的類是否加載、鏈接、初始化

虛擬機(jī)遇到一條new指令,首先去檢查這個(gè)指令的參數(shù)能否在Metaspace的常量池中定位到一個(gè)類的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類是否已經(jīng)被加載,解析和初始化(即判斷類元信息是否存在)

如果沒有,那么在雙親委派模式下,使用當(dāng)前類加載器以ClassLoader + 包名 + 類名為key進(jìn)行查找對(duì)應(yīng)的.class文件;

  • 如果沒有找到文件,則拋出ClassNotFoundException異常
  • 如果找到,則進(jìn)行類加載,并生成對(duì)應(yīng)的Class對(duì)象

2.為對(duì)象分配內(nèi)存

首先計(jì)算對(duì)象占用空間的大小,接著在堆中劃分一塊內(nèi)存給新對(duì)象。如果實(shí)例成員變量是引用變量,僅分配引用變量空間即可,即4個(gè)字節(jié)大小

如果內(nèi)存規(guī)整:虛擬機(jī)將采用的是指針碰撞法(Bump The Point)來為對(duì)象分配內(nèi)存。

  • 所有用過的內(nèi)存在一邊,空閑的內(nèi)存放另外一邊,中間放著一個(gè)指針作為分界點(diǎn)的指示器,分配內(nèi)存就僅僅是把指針指向空閑那邊挪動(dòng)一段與對(duì)象大小相等的距離罷了。如果垃圾收集器選擇的是Serial、ParNew這種基于壓縮算法的,虛擬機(jī)采用這種分配方式。一般使用帶Compact(整理)過程的收集器時(shí),使用指針碰撞

如果內(nèi)存不規(guī)整:虛擬機(jī)需要維護(hù)一個(gè)空閑列表(Free List)來為對(duì)象分配內(nèi)存

  • 已使用的內(nèi)存和未使用的內(nèi)存相互交錯(cuò),那么虛擬機(jī)將采用的是空閑列表來為對(duì)象分配內(nèi)存。意思是虛擬機(jī)維護(hù)了一個(gè)列表,記錄上那些內(nèi)存塊是可用的,再分配的時(shí)候從列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例,并更新列表上的內(nèi)容

選擇哪種分配方式由Java堆是否規(guī)整所決定,而Java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。

3.處理并發(fā)問題

  • 采用CAS失敗重試、區(qū)域加鎖保證更新的原子性
  • 每個(gè)線程預(yù)先分配一塊TLAB:通過設(shè)置-XX:+UseTLAB參數(shù)來設(shè)定

4.初始化分配到的內(nèi)存

所有屬性設(shè)置默認(rèn)值,保證對(duì)象實(shí)例字段在不賦值時(shí)可以直接使用

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

  • Mark Word:存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)元數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡、鎖狀態(tài)標(biāo)志、偏向線程ID等。這些信息直接影響對(duì)象的內(nèi)存管理、線程同步以及方法調(diào)用等操作
  • 類型指針:指向方法區(qū)中的類型信息,如類的元數(shù)據(jù)、方法表、常量池等。通過類型指針,JVM能夠快速定位到對(duì)象所屬類的詳細(xì)定義,實(shí)現(xiàn)方法調(diào)度、字段訪問等操作

6.執(zhí)行init方法進(jìn)行初始化

在Java程序的視角看來,初始化才正式開始。初始化成員變量,執(zhí)行實(shí)例化代碼塊,調(diào)用類的構(gòu)造方法,并把堆內(nèi)對(duì)象的首地址賦值給引用變量

對(duì)象初始化初始化順序?yàn)椋耗J(rèn) -> 顯式或靜態(tài)代碼塊 -> 構(gòu)造方法 -> setter

對(duì)象內(nèi)存布局

圖片圖片

對(duì)象頭

對(duì)象頭包含了兩部分,分別是運(yùn)行時(shí)元數(shù)據(jù)和類型指針。如果是數(shù)組,還需要記錄數(shù)組的長(zhǎng)度。

運(yùn)行時(shí)元數(shù)據(jù):

  • 哈希值(HashCode)
  • GC分代年齡
  • 鎖狀態(tài)標(biāo)志
  • 線程持有的鎖
  • 偏向線程ID
  • 偏向時(shí)間戳

類型指針:

  • 指向類元數(shù)據(jù)InstanceKlass,確定該對(duì)象所屬的類型

實(shí)例數(shù)據(jù)

它是對(duì)象真正存儲(chǔ)的有效信息,包括程序代碼中定義的各種類型的字段(包括從父類繼承下來的和本身擁有的字段)

  • 相同寬度的字段總是被分配在一起
  • 父類中定義的變量會(huì)出現(xiàn)在子類之前
  • 如果CompactFields參數(shù)為true(默認(rèn)為true):子類的窄變量可能插入到父類變量的空隙

對(duì)齊填充

不是必須的,也沒有特別的含義,僅僅起到占位符的作用

小結(jié):

public class Customer{
    int id = 1001;
    String name;
    Account acct;

    {
        name = "匿名客戶";
    }

    public Customer() {
        acct = new Account();
    }
}

public class CustomerTest{
    public static void main(string[] args){
        Customer cust = new Customer();
    }
}

圖片圖片

對(duì)象的訪問定位

圖片圖片

JVM是如何通過棧幀中的對(duì)象引用訪問到其內(nèi)部的對(duì)象實(shí)例?

句柄訪問

圖片圖片

reference中存儲(chǔ)穩(wěn)定句柄地址,對(duì)象被移動(dòng)(垃圾收集時(shí)移動(dòng)對(duì)象很普遍)時(shí)只會(huì)改變句柄中實(shí)例數(shù)據(jù)指針即可,reference本身不需要被修改

直接指針(HotSpot采用)

圖片圖片

直接指針是局部變量表中的引用,直接指向堆中的實(shí)例,在對(duì)象實(shí)例中有類型指針,指向的是方法區(qū)中的對(duì)象類型數(shù)據(jù)

直接內(nèi)存

不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是《Java虛擬機(jī)規(guī)范》中定義的內(nèi)存區(qū)域。直接內(nèi)存是在Java堆外的、直接向系統(tǒng)申請(qǐng)的內(nèi)存區(qū)間。來源于NIO,通過存在堆中的DirectByteBuffer操作Native內(nèi)存。通常,訪問直接內(nèi)存的速度會(huì)優(yōu)于Java堆,即讀寫性能高

非直接緩存區(qū)

圖片圖片

使用IO讀寫文件,需要與磁盤交互,需要由用戶態(tài)切換到內(nèi)核態(tài)。在內(nèi)核態(tài)時(shí),需要兩份內(nèi)存存儲(chǔ)重復(fù)數(shù)據(jù),效率低。

直接緩存區(qū)

圖片圖片

使用NIO時(shí),操作系統(tǒng)劃出的直接緩存區(qū)可以被java代碼直接訪問,只有一份。NIO適合對(duì)大文件的讀寫操作。

責(zé)任編輯:武曉燕 來源: 一安未來
相關(guān)推薦

2024-03-29 11:42:21

Java虛擬機(jī)

2024-04-03 13:49:00

Java虛擬機(jī)方法區(qū)

2012-11-14 09:57:46

JavaJava虛擬機(jī)JVM

2019-07-24 16:04:47

Java虛擬機(jī)并發(fā)

2024-03-26 07:30:07

Java虛擬機(jī)源文件

2016-09-01 12:37:13

OpenStack虛擬機(jī)Metadata

2022-07-06 08:05:52

Java對(duì)象JVM

2023-09-22 23:00:11

Java虛擬機(jī)

2024-07-25 14:18:29

2012-02-14 10:29:02

Java

2011-12-28 13:38:00

JavaJVM

2012-03-05 11:09:01

JavaClass

2022-08-21 16:52:27

Linux虛擬內(nèi)存

2023-09-19 22:47:39

Java內(nèi)存

2023-11-05 12:05:35

JVM內(nèi)存

2020-11-11 08:45:48

Java

2019-12-31 10:45:30

JavaVisualVM高并發(fā)

2020-11-04 15:35:13

Golang內(nèi)存程序員

2013-06-20 10:25:56

2009-06-04 16:27:39

Java虛擬機(jī)JVMGC
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 精品国产乱码久久久久久老虎 | 中文字幕在线免费视频 | 欧美a区 | 欧美激情国产日韩精品一区18 | 久久久久一区 | 国产精品视频一区二区三区 | 国产亚洲欧美在线视频 | 91美女在线观看 | 国产精品精品视频 | 日韩精品一区二区久久 | 一区二区三区四区不卡 | 91久久精品国产91久久性色tv | 最新av在线网址 | 精品久久av | 免费国产视频 | 婷婷毛片 | 国产一区二区三区精品久久久 | 午夜视频在线视频 | 国产一区二区电影 | 欧美一区二区免费 | 在线看无码的免费网站 | 久久久久久免费毛片精品 | 99精品在线观看 | 精品视频一区二区三区在线观看 | 亚洲精品日韩欧美 | 国产午夜精品久久久久免费视高清 | 欧美xxxⅹ性欧美大片 | 国产欧美在线观看 | www.4hu影院 | 日本免费在线观看视频 | www亚洲成人 | 91网站在线看 | 欧美美女一区二区 | 国产做a爱免费视频 | www四虎com | 久久激情av | 成人性视频在线播放 | 丁香综合 | 久久国产精品色av免费观看 | 日韩精品区 | 中文字幕二区三区 |