在Java世界中,萬(wàn)物皆對(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ì)象的訪問(wèn)定位和直接內(nèi)存展開介紹說(shuō)明。
圖片
最常見(jiàn)的方式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)限沒(méi)有要求:
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("反序列化完成!"); }}
第三方庫(kù) Objenesis:
Objenesis objenesis = new ObjenesisStd();ObjectInstantiator<Student> instantiator = objenesis.getInstantiatorOf(Student.class);Student st = instantiator.newInstance();
javap -v -p Student.class:
圖片
注意:
<init>與<clinit>的區(qū)別:前者是一個(gè)類的構(gòu)造器在字節(jié)碼中對(duì)應(yīng)的方法,后者習(xí)慣被稱為類構(gòu)造器方法,他會(huì)在類加載的初始化階段對(duì)類的靜態(tài)部分進(jìn)行初始化【如靜態(tài)代碼塊,靜態(tài)成員變量等】
1.判斷對(duì)象對(duì)應(yīng)的類是否加載、鏈接、初始化
虛擬機(jī)遇到一條new指令,首先去檢查這個(gè)指令的參數(shù)能否在Metaspace的常量池中定位到一個(gè)類的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類是否已經(jīng)被加載,解析和初始化(即判斷類元信息是否存在)
如果沒(méi)有,那么在雙親委派模式下,使用當(dāng)前類加載器以ClassLoader + 包名 + 類名為key進(jìn)行查找對(duì)應(yīng)的.class文件;
2.為對(duì)象分配內(nèi)存
首先計(jì)算對(duì)象占用空間的大小,接著在堆中劃分一塊內(nèi)存給新對(duì)象。如果實(shí)例成員變量是引用變量,僅分配引用變量空間即可,即4個(gè)字節(jié)大小
如果內(nèi)存規(guī)整:虛擬機(jī)將采用的是指針碰撞法(Bump The Point)來(lái)為對(duì)象分配內(nèi)存。
如果內(nèi)存不規(guī)整:虛擬機(jī)需要維護(hù)一個(gè)空閑列表(Free List)來(lái)為對(duì)象分配內(nèi)存
選擇哪種分配方式由Java堆是否規(guī)整所決定,而Java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。
3.處理并發(fā)問(wèn)題
4.初始化分配到的內(nèi)存
所有屬性設(shè)置默認(rèn)值,保證對(duì)象實(shí)例字段在不賦值時(shí)可以直接使用
5.設(shè)置對(duì)象的對(duì)象頭
6.執(zhí)行init方法進(jìn)行初始化
在Java程序的視角看來(lái),初始化才正式開始。初始化成員變量,執(zhí)行實(shí)例化代碼塊,調(diào)用類的構(gòu)造方法,并把堆內(nèi)對(duì)象的首地址賦值給引用變量
對(duì)象初始化初始化順序?yàn)椋耗J(rèn) -> 顯式或靜態(tài)代碼塊 -> 構(gòu)造方法 -> setter
圖片
對(duì)象頭包含了兩部分,分別是運(yùn)行時(shí)元數(shù)據(jù)和類型指針。如果是數(shù)組,還需要記錄數(shù)組的長(zhǎng)度。
運(yùn)行時(shí)元數(shù)據(jù):
類型指針:
它是對(duì)象真正存儲(chǔ)的有效信息,包括程序代碼中定義的各種類型的字段(包括從父類繼承下來(lái)的和本身?yè)碛械淖侄危?span style="display:none">iy628資訊網(wǎng)——每日最新資訊28at.com
不是必須的,也沒(méi)有特別的含義,僅僅起到占位符的作用
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(); }}
圖片
圖片
JVM是如何通過(guò)棧幀中的對(duì)象引用訪問(wèn)到其內(nèi)部的對(duì)象實(shí)例?
圖片
reference中存儲(chǔ)穩(wěn)定句柄地址,對(duì)象被移動(dòng)(垃圾收集時(shí)移動(dòng)對(duì)象很普遍)時(shí)只會(huì)改變句柄中實(shí)例數(shù)據(jù)指針即可,reference本身不需要被修改
圖片
直接指針是局部變量表中的引用,直接指向堆中的實(shí)例,在對(duì)象實(shí)例中有類型指針,指向的是方法區(qū)中的對(duì)象類型數(shù)據(jù)
不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是《Java虛擬機(jī)規(guī)范》中定義的內(nèi)存區(qū)域。直接內(nèi)存是在Java堆外的、直接向系統(tǒng)申請(qǐng)的內(nèi)存區(qū)間。來(lái)源于NIO,通過(guò)存在堆中的DirectByteBuffer操作Native內(nèi)存。通常,訪問(wèn)直接內(nèi)存的速度會(huì)優(yōu)于Java堆,即讀寫性能高
圖片
使用IO讀寫文件,需要與磁盤交互,需要由用戶態(tài)切換到內(nèi)核態(tài)。在內(nèi)核態(tài)時(shí),需要兩份內(nèi)存存儲(chǔ)重復(fù)數(shù)據(jù),效率低。
圖片
使用NIO時(shí),操作系統(tǒng)劃出的直接緩存區(qū)可以被java代碼直接訪問(wèn),只有一份。NIO適合對(duì)大文件的讀寫操作。
本文鏈接:http://www.www897cc.com/showinfo-26-82512-0.html深入理解Java虛擬機(jī):對(duì)象實(shí)例化及直接內(nèi)存詳解
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com