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

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

云計算 虛擬化
在Java中我們用使用一個類,很多時候是創(chuàng)建這個類的一個實例,也就是常說的創(chuàng)建一個對象。其實在Java程序運行過程中,無時無刻都有對象被創(chuàng)建出來。

勿在流沙筑高臺,出來混遲早要還的。

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

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

本文地圖:

 

Java內存管理-一文掌握虛擬機創(chuàng)建對象的秘密(九)

 

一、給你創(chuàng)建一個對象

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

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

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

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

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

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

第三:內存分配完成,虛擬機需要將分配的內存空間都初始化為零值(零值這個概念之前文章也介紹過,這里就不再說明),這一步的操作保證了對象的實例字段在Java代碼中可以不賦初始值就直接使用,因為程序能訪問這些字段的數(shù)據(jù)類型對應的零值。 記住:要new對象,虛擬機會幫你為對象的實例字段自動賦予零值!

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

上面工作都完成之后,在虛擬機看來,一個對象就已經產生了。但是從Java程序的角度看,對象的創(chuàng)建才剛剛開始,因為 方法還還沒有執(zhí)行,所有的字段都是為零值。所以一般來說,在new指令之后會接著執(zhí)行方法,把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全產生出來!

記住:對象不是你想new,想new就可以new的!

下面用通過圖解的例子簡單說明(版本jdk1.7):

第一: 一個PrettyGirl類!

  1. public class PrettyGirl { 
  2.  /** 
  3.  * 姑娘姓字名誰 
  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內存管理-一文掌握虛擬機創(chuàng)建對象的秘密(九)

 

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

第二:實例化new兩個漂亮女孩!

  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. ----打印結果:-------- 
  13. PrettyGirl{name='Alice', age=18, marry=false---changsha 
  14. PrettyGirl{name='Alexia', age=28, marry=false}----changsha  

 

Java內存管理-一文掌握虛擬機創(chuàng)建對象的秘密(九)

 

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

堆存放對象持有的數(shù)據(jù),同時保持對原類的引用。可以簡單的理解為對象屬性的值保存在堆中,對象調用的方法保存在方法區(qū)。

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

tips: 回顧

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

2、在棧中會有兩個異常:

  • 如果線程請求的棧的深度大于虛擬機所允許的最大深度,將拋出StackOverflowError 異常(遞歸可能會導致此異常)!
  • 如果虛擬機在擴展棧時候無法申請到足夠的內存空間,則拋出OutOfMemoryError異常。

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

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

二、對象的內存布局

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

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

那下面就對這三塊區(qū)域進行簡單介紹:

1、對象頭- 還是一個看臉的時代!

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

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

注: 上面的幾個點,要結合和關聯(lián)其他相關知識,理解會更加深入一點。

如 哈希碼hashCode,對下面兩個問題如果你又自己的一些思考,歡迎留言探討!

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

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

第二部分是類型指針,即對象指向它的類元數(shù)據(jù)的指針,虛擬機通過指針來確定這個對象是那個類的實例。(就如我們上圖的箭頭,可以簡單理解為指針!)

說明:

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

(2)、如果對象是一個Java數(shù)組,那在對象頭中還必須有一塊用于記錄數(shù)組長度的的數(shù)據(jù),因為虛擬機可以通過普通Java對象的元數(shù)據(jù)確定Java對象的大小,但是從數(shù)組的元數(shù)據(jù)卻無法確定數(shù)組的大小。

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

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

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

3、對齊填充-對齊填充成為標準網紅!

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

(類比記憶對齊填充,由于審美的標準,有一些人天生就是俊俏的臉蛋和好的身材,不需要進行其他的填充,有一些人可能有好看的臉蛋,但是某些地方和標準還差點意思,就需要填充來達到標準)

tips:字節(jié)

字節(jié)(byte)計算機里用來存儲空間的基本計量單位。8個二進制位(bit)構成了一個字節(jié)(byte)即1byte=8bit。

三、如何“約”(定位)一個對象

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

在Java程序中需要通過棧上的reference數(shù)據(jù)來操作堆上的具體對象(如開篇的圖示,棧上面的引入指向堆中具體對象)。但是由于Reference類型在Java虛擬機規(guī)范中只規(guī)定了一個指向對象的引用,并沒有定義這個引用應該通過何種方式去定位、訪問堆中的對象的具體位置,所以對象訪問方式也是取決于虛擬機實現(xiàn)而定的。

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

第一:句柄

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

 

Java內存管理-一文掌握虛擬機創(chuàng)建對象的秘密(九)

 

第二:直接指針

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

 

Java內存管理-一文掌握虛擬機創(chuàng)建對象的秘密(九)

 

兩種方式都各自優(yōu)勢,簡單總結:

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

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

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

四、本文總結

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

 

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2018-09-11 15:01:22

Java虛擬機組成

2021-01-07 10:04:24

容器虛擬機測試

2019-07-18 15:01:43

Linux虛擬機宿主機

2022-12-20 07:39:46

2023-12-21 17:11:21

Containerd管理工具命令行

2023-07-31 07:25:27

2022-10-21 17:24:34

契約測試定位

2021-05-12 18:22:36

Linux 內存管理

2024-11-19 09:00:00

Pythondatetime模塊

2025-04-18 05:50:59

Spring接口Aware

2019-04-13 15:23:48

網絡模型虛擬機

2023-10-24 11:44:21

2023-12-15 09:45:21

阻塞接口

2020-10-09 07:56:52

Linux

2023-03-03 08:10:15

TerraformAzure虛擬機

2010-02-24 16:37:27

Ubuntu VMwa

2013-01-18 14:04:58

VMwareWorkstation

2017-11-28 15:20:27

Python語言編程

2022-08-15 15:39:23

JavaScript面向對象數(shù)據(jù)

2020-12-18 11:54:22

Linux系統(tǒng)架構
點贊
收藏

51CTO技術棧公眾號