偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一文掌握虛擬機(jī)創(chuàng)建對(duì)象的秘密

云計(jì)算 虛擬化
在Java中我們用使用一個(gè)類,很多時(shí)候是創(chuàng)建這個(gè)類的一個(gè)實(shí)例,也就是常說(shuō)的創(chuàng)建一個(gè)對(duì)象。其實(shí)在Java程序運(yùn)行過(guò)程中,無(wú)時(shí)無(wú)刻都有對(duì)象被創(chuàng)建出來(lái)。

勿在流沙筑高臺(tái),出來(lái)混遲早要還的。

  • 做一個(gè)積極的人
  • 編碼、改bug、提升自己
  • 我有一個(gè)樂(lè)園,面向編程,春暖花開(kāi)!
  • [[320720]]

本文主要內(nèi)容講解HotSpot虛擬機(jī)在Java堆中對(duì)象是如何創(chuàng)建、內(nèi)存分配布局和訪問(wèn)方式。

本文地圖:

 

Java內(nèi)存管理-一文掌握虛擬機(jī)創(chuàng)建對(duì)象的秘密(九)

 

一、給你創(chuàng)建一個(gè)對(duì)象

如果你是一直從第一季看過(guò)來(lái)的,那一定知道前面有個(gè)地方講過(guò)類的整個(gè)生命周期,之前只是講到了初始化階段,類是如何使用和類是如何被卸載還沒(méi)有進(jìn)行講解!那本文就簡(jiǎn)單介紹一下類的使用,我們new 一個(gè) “如花” 似玉的girl!

這里再回顧一下,類從被加載到虛擬機(jī)內(nèi)存中開(kāi)始,到卸載出內(nèi)存為止,它的生命周期包括了七個(gè)階段:

  • 加載(Loading)
  • 驗(yàn)證(Verification)
  • 準(zhǔn)備(Preparation)
  • 解析(Resolution)
  • 初始化(Initialization)
  • 使用(Using)
  • 卸載(Unloading)

在Java中我們用使用一個(gè)類,很多時(shí)候是創(chuàng)建這個(gè)類的一個(gè)實(shí)例,也就是常說(shuō)的創(chuàng)建一個(gè)對(duì)象。其實(shí)在Java程序運(yùn)行過(guò)程中,無(wú)時(shí)無(wú)刻都有對(duì)象被創(chuàng)建出來(lái)。創(chuàng)建對(duì)象(如克隆、反序列化)通常僅僅是一個(gè)new關(guān)鍵字而已。但是在Java虛擬機(jī)中一個(gè)對(duì)象(只是普通的java對(duì)象,不包括數(shù)組和Class對(duì)象等)的創(chuàng)建是怎么一個(gè)過(guò)程呢?

第一:虛擬機(jī)遇到一條new指令時(shí),首先會(huì)去檢查這個(gè)指令的參數(shù)是否能夠在常量池中定位到一個(gè)類的符號(hào)引用。然后檢查這個(gè)符號(hào)引用代表的類是否已經(jīng)被加載、解析和初始化過(guò)。如果沒(méi)有進(jìn)行類加載則執(zhí)行相應(yīng)的類加載的過(guò)程。 記住:要new對(duì)象,要先加載類!

第二:類加載檢查通過(guò)后,虛擬機(jī)將為新生的對(duì)象分配內(nèi)存。對(duì)象所需的內(nèi)存大小在類加載的時(shí)候便可以完全確定(如何確定對(duì)象的下文說(shuō)明) 。為對(duì)象分配內(nèi)存的任務(wù)等同于把一塊確定大小的內(nèi)存從Java堆中劃分出來(lái)。分配方式有 “指針碰撞” 和 “空閑列表” 兩種,選擇那種分配方式由 Java 堆是否規(guī)整決定,而Java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定(對(duì)象在堆上的劃分,這是個(gè)復(fù)雜的問(wèn)題,后文繼續(xù)探討,這里只要明白是在對(duì)象是在堆上分配內(nèi)存即可)。 記?。阂猲ew對(duì)象,要有先分配內(nèi)存空間!

第三:內(nèi)存分配完成,虛擬機(jī)需要將分配的內(nèi)存空間都初始化為零值(零值這個(gè)概念之前文章也介紹過(guò),這里就不再說(shuō)明),這一步的操作保證了對(duì)象的實(shí)例字段在Java代碼中可以不賦初始值就直接使用,因?yàn)槌绦蚰茉L問(wèn)這些字段的數(shù)據(jù)類型對(duì)應(yīng)的零值。 記?。阂猲ew對(duì)象,虛擬機(jī)會(huì)幫你為對(duì)象的實(shí)例字段自動(dòng)賦予零值!

第四:虛擬機(jī)要對(duì)對(duì)象進(jìn)行必要的設(shè)置,如這個(gè)對(duì)象是哪個(gè)類的實(shí)例、如何才能找到類的元數(shù)據(jù)信息(JDK7是方法區(qū)保存)、對(duì)象的哈希碼、對(duì)象的GC分代年齡等信息。這些信息都存放在對(duì)象的對(duì)象頭(Object Header)中。

上面工作都完成之后,在虛擬機(jī)看來(lái),一個(gè)對(duì)象就已經(jīng)產(chǎn)生了。但是從Java程序的角度看,對(duì)象的創(chuàng)建才剛剛開(kāi)始,因?yàn)?init> 方法還還沒(méi)有執(zhí)行,所有的字段都是為零值。所以一般來(lái)說(shuō),在new指令之后會(huì)接著執(zhí)行方法,把對(duì)象按照程序員的意愿進(jìn)行初始化,這樣一個(gè)真正可用的對(duì)象才算完全產(chǎn)生出來(lái)!

記?。簩?duì)象不是你想new,想new就可以new的!

下面用通過(guò)圖解的例子簡(jiǎn)單說(shuō)明(版本jdk1.7):

第一: 一個(gè)PrettyGirl類!

  1. public class PrettyGirl { 
  2.  /** 
  3.  * 姑娘姓字名誰(shuí) 
  4.  */ 
  5.  String name
  6.  /** 
  7.  * 芳齡幾何 
  8.  */ 
  9.  int age; 
  10.  /** 
  11.  * 家住何方 
  12.  */ 
  13.  static String address; 
  14.  /** 
  15.  * 可曾婚配否 
  16.  */ 
  17.  boolean marry; 
  18.  void sayHello(){ 
  19.  System.out.println("Hello..."); 
  20.  } 
  21.  @Override 
  22.  public String toString() { 
  23.  return "PrettyGirl{" + 
  24.  "name='" + name + '\'' + 
  25.  ", age=" + age + 
  26.  ", marry=" + marry + 
  27.  '}'
  28.  } 

 

Java內(nèi)存管理-一文掌握虛擬機(jī)創(chuàng)建對(duì)象的秘密(九)

 

方法區(qū)除了保存類的結(jié)構(gòu),還保存靜態(tài)屬性與靜態(tài)方法。編寫(xiě)中小型程序時(shí),一般不會(huì)造成方法區(qū)的內(nèi)存溢出!在JDK1.8 沒(méi)有方法區(qū)的概念,前面文章中也有提到,這里為了講解使用圖解還是JDK1.7!

第二:實(shí)例化new兩個(gè)漂亮女孩!

  1. public static void main(String[] args) { 
  2.  PrettyGirl pg1 = new PrettyGirl(); 
  3.  pg1.name = "Alice"
  4.  pg1.age = 18; 
  5.  pg1.address = "changsha"
  6.  PrettyGirl pg2 = new PrettyGirl(); 
  7.  pg2.name = "Alexia"
  8.  pg2.age = 28; 
  9.  System.out.println(pg1 + " ---" + pg1.address); 
  10.  System.out.println(pg2 + "----" + pg2.address); 
  11. }  
  12. ----打印結(jié)果:-------- 
  13. PrettyGirl{name='Alice', age=18, marry=false---changsha 
  14. PrettyGirl{name='Alexia', age=28, marry=false}----changsha  

 

Java內(nèi)存管理-一文掌握虛擬機(jī)創(chuàng)建對(duì)象的秘密(九)

 

在棧內(nèi)存為 pg1 變量申請(qǐng)一個(gè)空間,在堆內(nèi)存為PrettyGirl對(duì)象申請(qǐng)空間,初始化完畢后將其地址值返回給pg1 ,通過(guò)pg1 .name和pg1 .age修改其值,靜態(tài)的變量address是類公有的!

堆存放對(duì)象持有的數(shù)據(jù),同時(shí)保持對(duì)原類的引用??梢院?jiǎn)單的理解為對(duì)象屬性的值保存在堆中,對(duì)象調(diào)用的方法保存在方法區(qū)。

從上圖也可以看到有一個(gè)區(qū)域是棧,在程序運(yùn)行的時(shí)候,每當(dāng)遇到方法 調(diào)用時(shí)候,Java虛擬機(jī)就會(huì)在棧中劃分一塊內(nèi)存稱為棧幀(線程私有,堆和方法區(qū)線程共享的)。就如上面的程序,在調(diào)用main方法的時(shí)候,會(huì)創(chuàng)建一下棧,棧幀中的內(nèi)存供局部變量(包括基本類型和引用類型)使用,基本類型和引用類型后文會(huì)詳情介紹。當(dāng)方法調(diào)用結(jié)束后,虛擬機(jī)會(huì)回收次棧幀占用的內(nèi)存。

tips: 回顧

1、堆內(nèi)存溢出會(huì)發(fā)生 OutOfMemoryError 錯(cuò)誤,提示信息“Java heap Space”。

2、在棧中會(huì)有兩個(gè)異常:

  • 如果線程請(qǐng)求的棧的深度大于虛擬機(jī)所允許的最大深度,將拋出StackOverflowError 異常(遞歸可能會(huì)導(dǎo)致此異常)!
  • 如果虛擬機(jī)在擴(kuò)展棧時(shí)候無(wú)法申請(qǐng)到足夠的內(nèi)存空間,則拋出OutOfMemoryError異常。

3、如果有方法區(qū) 也會(huì)出現(xiàn)OutOfMemoryError 錯(cuò)誤,提示信息 “PermGen space”。(JDK8 后無(wú)此錯(cuò)誤提示)

每個(gè)區(qū)域都有一些參數(shù)可以設(shè)置,參數(shù)學(xué)習(xí)續(xù)持續(xù)更新!

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

感慨,創(chuàng)建一個(gè)對(duì)象還是挺不容易的!

在HotSpot虛擬機(jī)中,對(duì)象在內(nèi)存中的布局可以分為3塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance data)和對(duì)象填充(Padding)。

那下面就對(duì)這三塊區(qū)域進(jìn)行簡(jiǎn)單介紹:

1、對(duì)象頭- 還是一個(gè)看臉的時(shí)代!

對(duì)象頭包括兩部分信息。第一部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如

  • 哈希碼(HashCode),一個(gè)對(duì)象的hashcode是唯一的,如判斷一個(gè)對(duì)象是不是單例的!
  • GC分代年齡(標(biāo)明是新生代還是老年代..)
  • 鎖狀態(tài)標(biāo)志、線程持有的鎖、偏向線程ID(多線程,同步的時(shí)候用到)
  • 其他等等….

注: 上面的幾個(gè)點(diǎn),要結(jié)合和關(guān)聯(lián)其他相關(guān)知識(shí),理解會(huì)更加深入一點(diǎn)。

如 哈希碼hashCode,對(duì)下面兩個(gè)問(wèn)題如果你又自己的一些思考,歡迎留言探討!

  • 重寫(xiě)了equals 必須要重寫(xiě)hashcode,思考一下,為什么?如果不重寫(xiě)在使用HashMap的時(shí)候會(huì)有出現(xiàn)什么問(wèn)題?
  • HashMap中相同key存入數(shù)據(jù)不替換,而是進(jìn)行疊加存儲(chǔ),怎么實(shí)現(xiàn)?

問(wèn)題2提示:只要重寫(xiě)了key的hashCode()和Map的put()方法,其實(shí)就可以實(shí)現(xiàn)對(duì)于相同key下疊加存儲(chǔ)不同的value了。

第二部分是類型指針,即對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)指針來(lái)確定這個(gè)對(duì)象是那個(gè)類的實(shí)例。(就如我們上圖的箭頭,可以簡(jiǎn)單理解為指針!)

說(shuō)明:

(1)、并不是所有的虛擬機(jī)實(shí)現(xiàn)都是必須在對(duì)象數(shù)據(jù)上保留類型指針,也就是查找對(duì)象的元數(shù)據(jù)并一定經(jīng)過(guò)對(duì)象本身!

(2)、如果對(duì)象是一個(gè)Java數(shù)組,那在對(duì)象頭中還必須有一塊用于記錄數(shù)組長(zhǎng)度的的數(shù)據(jù),因?yàn)樘摂M機(jī)可以通過(guò)普通Java對(duì)象的元數(shù)據(jù)確定Java對(duì)象的大小,但是從數(shù)組的元數(shù)據(jù)卻無(wú)法確定數(shù)組的大小。

2、實(shí)例數(shù)據(jù)-了解了外在美,還要注重內(nèi)在美!

實(shí)例數(shù)據(jù)部分是對(duì)象真正存儲(chǔ)的有效信息,也就是程序代碼中定義的各種類型的字段內(nèi)容。

不論是從父類繼承下來(lái)的,還是在子類中定義的,都需要記錄起來(lái)。記錄的存儲(chǔ)順序會(huì)受到虛擬機(jī)分配策略參數(shù)和字段在Java源碼中的定義的順序相關(guān)。

3、對(duì)齊填充-對(duì)齊填充成為標(biāo)準(zhǔn)網(wǎng)紅!

對(duì)象的填充并不是必然存在的,也沒(méi)有特別的含義,它僅僅起著占位符的作用!由于HotSpot VM的自動(dòng)內(nèi)存管理系統(tǒng)要求兌現(xiàn)的起始地址必須是8字節(jié)的整數(shù)倍,也就是說(shuō)對(duì)象的大小必須是8字節(jié)的整數(shù)倍。而對(duì)象頭部分正好是8字節(jié)的整數(shù)倍,因此當(dāng)對(duì)象實(shí)例數(shù)據(jù)部分沒(méi)有對(duì)齊時(shí)候,就需要填充來(lái)補(bǔ)全。

(類比記憶對(duì)齊填充,由于審美的標(biāo)準(zhǔn),有一些人天生就是俊俏的臉蛋和好的身材,不需要進(jìn)行其他的填充,有一些人可能有好看的臉蛋,但是某些地方和標(biāo)準(zhǔn)還差點(diǎn)意思,就需要填充來(lái)達(dá)到標(biāo)準(zhǔn))

tips:字節(jié)

字節(jié)(byte)計(jì)算機(jī)里用來(lái)存儲(chǔ)空間的基本計(jì)量單位。8個(gè)二進(jìn)制位(bit)構(gòu)成了一個(gè)字節(jié)(byte)即1byte=8bit。

三、如何“約”(定位)一個(gè)對(duì)象

認(rèn)識(shí)了一個(gè)對(duì)象后,不能總是聊微信,也要約一下吃個(gè)飯啥的! 那在Java中建立了一個(gè)對(duì)象,那肯定是要使用對(duì)象的。Java程序是如果找到具體的對(duì)象的呢?

在Java程序中需要通過(guò)棧上的reference數(shù)據(jù)來(lái)操作堆上的具體對(duì)象(如開(kāi)篇的圖示,棧上面的引入指向堆中具體對(duì)象)。但是由于Reference類型在Java虛擬機(jī)規(guī)范中只規(guī)定了一個(gè)指向?qū)ο蟮囊?,并沒(méi)有定義這個(gè)引用應(yīng)該通過(guò)何種方式去定位、訪問(wèn)堆中的對(duì)象的具體位置,所以對(duì)象訪問(wèn)方式也是取決于虛擬機(jī)實(shí)現(xiàn)而定的。

目前主流的訪問(wèn)方式有使用句柄和直接指針兩種。

第一:句柄

使用句柄訪問(wèn),在Java對(duì)中將會(huì)劃分出一塊內(nèi)存來(lái)作為句柄池,reference中存儲(chǔ)的就是對(duì)象的句柄地址,而句柄中包含了對(duì)象的實(shí)例數(shù)據(jù)與類型數(shù)據(jù)各自 的具體地址信息,如圖,

 

Java內(nèi)存管理-一文掌握虛擬機(jī)創(chuàng)建對(duì)象的秘密(九)

 

第二:直接指針

使用直接指針,在Java堆對(duì)象的布局中就必須考慮如果放置訪問(wèn)類型數(shù)組的相關(guān)信息,而reference中存儲(chǔ)的直接就是對(duì)象的地址,如圖:

 

Java內(nèi)存管理-一文掌握虛擬機(jī)創(chuàng)建對(duì)象的秘密(九)

 

兩種方式都各自優(yōu)勢(shì),簡(jiǎn)單總結(jié):

句柄:最大的好處就是reference中存儲(chǔ)的是穩(wěn)定的句柄地址,在對(duì)象被移動(dòng)(垃圾收集移動(dòng)對(duì)象是非常普通的行為)時(shí)只會(huì)改變句柄中的實(shí)例數(shù)據(jù)指針,而Reference本身不需要修改。

直接指針:最大的好處就是速度更快,它節(jié)省一次指針定位的開(kāi)銷,在Java中對(duì)象的訪問(wèn)是非常頻繁的,因此能減少這類開(kāi)銷對(duì)提高性能還是非常客觀的。

虛擬機(jī)Hotspot使用的就是直接指針這種方式。但是其他的語(yǔ)言和框架中使用句柄的情況也很常見(jiàn)!

四、本文總結(jié)

本文主要整理了Java中一個(gè)對(duì)象的創(chuàng)建,對(duì)象的內(nèi)存布局以及如何定位一個(gè)對(duì)象! 也讓我們知道對(duì)象不是你想new就可以new的,new出的對(duì)象想要“約”也是有不同方式的。

 

責(zé)任編輯:武曉燕 來(lái)源: 今日頭條
相關(guān)推薦

2018-09-11 15:01:22

Java虛擬機(jī)組成

2021-01-07 10:04:24

容器虛擬機(jī)測(cè)試

2019-07-18 15:01:43

Linux虛擬機(jī)宿主機(jī)

2023-07-31 07:25:27

2023-12-21 17:11:21

Containerd管理工具命令行

2022-12-20 07:39:46

2022-10-21 17:24:34

契約測(cè)試定位

2021-05-12 18:22:36

Linux 內(nèi)存管理

2025-04-18 05:50:59

Spring接口Aware

2024-11-19 09:00:00

Pythondatetime模塊

2025-07-04 07:26:58

HotSpot虛擬機(jī)對(duì)象

2023-10-24 11:44:21

2025-05-21 09:32:28

2023-12-15 09:45:21

阻塞接口

2020-10-09 07:56:52

Linux

2019-04-13 15:23:48

網(wǎng)絡(luò)模型虛擬機(jī)

2017-11-28 15:20:27

Python語(yǔ)言編程

2020-12-18 11:54:22

Linux系統(tǒng)架構(gòu)

2021-06-04 09:35:05

Linux字符設(shè)備架構(gòu)

2021-02-22 09:05:59

Linux字符設(shè)備架構(gòu)
點(diǎn)贊
收藏

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