java創建對象的過程詳解(從內存角度分析)

2021-01-07 愚公要移山1

說在前面的幾句廢話

前幾天我的文章一直沒有更新,大概斷了4天左右,因為外出有事,所以給耽擱了,有朋友建議,我可以集中花幾天,每天只寫文章,這樣以後覺得哪裡不妥還可以修改,覺得言之有理就果斷採納了,前一個月開始做自媒體寫文章,這段時間有很多人給我提出了寶貴的意見,真的感謝大家的支持。同時,今天也是建黨節,作為一個愛國青年,真心祝願祖國越來越好。

廢話可以不看,下面就開始今天的文章。java對象的創建操作其實我在《JVM系列之類的加載機制》一文曾經提到過,包含兩個過程:類的初始化和實例化。為此為了理解的深入,我們還需要再來看一下類的生命周期。一張圖表示:

從上面我們可以看到,對象的創建其實包含了初始化和使用兩個階段。有了這個印象之後,我們就能開始今天的文章了。先給出這篇文章的大致脈絡:

首先,介紹一下java中對象的創建基本知識然後,介紹一下對象初始化的順序接下來,介紹一下創建對象的幾種方式最後,進行一個總結。(從內存角度去分析:重點)重點

一、基本知識

我們知道,一個對象的創建過程包含兩個過程:初始化和實例化

我們在使用一個對象時,JVM首先會檢查相關類型是否已經加載並初始化,如果沒有,則JVM立即進行加載並調用類構造器完成類的初始化。在類初始化過程中或初始化完畢後,根據具體情況才會去對類進行實例化。

實例化時候,java虛擬機就會為其分配內存來存放自己及其從父類繼承過來的實例變量。在為這些實例變量分配內存的同時,這些實例變量先會被賦予默認值(零值)。在內存分配完成之後,Java虛擬機才會對新創建的對象賦予我們程式設計師給定的值。

小結:創建一個對象包含下面兩個過程:

1、類構造器完成類初始化(分配內存、賦予默認值)

2、類實例化(賦予給定值)

二、類初始化

下面我們直接給出一個例子看一下java是如何初始化的。我們知道一個類中,往往包含靜態變量、靜態代碼塊、變量、普通方法、構造方法等信息。那麼他們是如何初始化的呢?

輸出:

上面這個例子比較簡單,我們再來看看帶有父類和接口的情況。

第一步:定義一個父類

第二步:定義一個子類實現

第三步:看結果

小結,類的初始化順序,這樣看確實不好記,不過沒有繼承關係的我們都能很好的看到。帶繼承關係的,使用網上一張圖來表示:

OK,類的初始化中的知識點基本上就是初始化的順序。

三、創建對象的幾種方式

其實對象的初始化就是在創建對象的時候由jvm完成的。對於創建對象,主要是研究創建對象的幾種方式。下面一一的解答.這裡給出6種方式,面試的時候足夠你zhuangbility。

使用new關鍵字Class對象的newInstance()方法構造函數對象的newInstance()方法對象反序列化Object對象的clone()方法使用Unsafe類創建對象最後再揭曉。。。OK,先認識一個,下面一個一個看。

(1)使用new關鍵字

(2)class的newInstance()方法

首先我們通過Class.forName()動態的加載類的Class對象,

然後通過newInstance()方法獲得Test類的對象

(3)構造函數的newInstance()方法

類Constructor也有newInstance方法,這一點和Class有點像。從它的名字可以看出它與Class的不同,Class是通過類來創建對象,而Constructor則是通過構造器。

(4)序列化

首先我們要對Test實現Serializable接口。然後開始序列化數據。最後得到反序列化的對象。

(5)clone方式

Object對象中存在clone方法,它的作用是創建一個對象的副本。

(6)使用Unsafe類創建對象

Unsafe類使Java擁有了像C語言的指針一樣操作內存空間的能力,同時也帶來了指針的問題。過度的使用Unsafe類會使得出錯的機率變大,因此Java官方並不建議使用的,官方文檔也幾乎沒有。Oracle正在計劃從Java 9中去掉Unsafe類,如果真是如此影響就太大了。

我們無法直接創建Unsafe對象。這裡我們使用反射方法得到

拿到這個對象後,調用其中的native方法allocateInstance 創建一個對象實例

Object event = unsafe.allocateInstance(Test.class);

四、總結

我們想要創建一個對象。基本上就是java虛擬機分配內存的過程。因此我們可以先回顧一下java程序的執行過程。給一張網上的圖,寫的很清晰

一個例子去解釋:(摘自我之前的文章《java8的內存結構》)

然後我們測試一下:

我們分析一下這個過程

第一步,JVM去方法區尋找Test類的代碼信息,如果有直接調用,沒有的話使用類的加載機制把類加載進來。同時把靜態變量、靜態方法、常量加載進來。這裡加載的是(「馮冬冬的IT技術棧」,「馮XX」);這是因為字符串是常量,age中的18是基本類型。

第二步,jvm進入main方法,看到Person person=new Person()。首先分析Person這個類,同樣的尋找Person類的代碼信息,有就加載,沒有的話類加載機制加載進來。同時也加載靜態變量、靜態方法、常量(「我正在走路。。。」)

第三步,jvm接下來看到了person,person在main方法內部,因而是局部變量,存放在棧空間中。

第四步,jvm接下來看到了new Person()。new出的對象(實例),存放在堆空間中。

第五步,jvm接下來看到了「=」,把new Person的地址告訴person變量,person通過四字節的地址(十六進位),引用該實例。 是不是有點暈,別著急,畫個圖看一下。

第六步,jvm看到person.name = 「馮冬冬的IT技術棧」;person通過引用new Person實例的name屬性,該name屬性通過地址指向常量池的"馮冬冬的IT技術棧"。

第七步,jvm看到person.age = 18; person的age屬性是基本數據類型,直接賦值。

第八步,jvm看到person.walk(); 調用實例的方法時,並不會在實例對象中生成一個新的方法,而是通過地址指向方法區中類信息的方法。走到這一步再看看圖怎麼變化的

大多數人基本上都能看懂。創建一個對象的過程基本上就是這。

如有問題還請批評指正。謝謝

相關焦點

  • 對象的創建過程
    我們使用main函數創建School的一個對象,那麼這個過程發生了哪些事情?在JVM內存中多了什麼呢?讓我們一起看下吧!,大致可以分為以下5步:在詳細了解這5個步驟之前我們再詳細聊一下對象頭,在synchronized鎖升級過程分析的時候我們已經初步接觸過它。
  • 淺析java內存管理機制
    中,內存管理由JVM完全負責,java中的「垃圾回收器」負責自動回收無用對象佔據的內存資源,這樣可以大大減少程序猿在內存管理上花費的時間,可以更集中於業務邏輯和具體功能實現;但這並不是說java有了垃圾回收器程序猿就可以高枕無憂,將內存管理拋之腦外了!
  • 深入分析Java中的關鍵字static
    這篇文章就把java中static關鍵字的使用方法的原理進行一個深入的分析。一、static關鍵字的基本用法1、static關鍵字基本概念我們可以一句話來概括:方便在沒有創建對象的情況下來進行調用。也就是說:被static關鍵字修飾的不需要創建對象去調用,直接根據類名就可以去訪問。
  • java內存溢出問題分析過程二(附MAT超全操作文檔)
    在Memory Analyzer工具中提供了對象圖的支配樹。將對象引用圖轉換為dominator tree可以讓輕鬆地確定Retained heap最大內存塊和對象之間保持活動的依賴關係,相當於主宰了整個JVM的感覺。我們把java對象之間的引用關係看做一張有向圖,如果所有指向Y的對象路徑都要經過X,則我們說X支配Y。
  • EffectiveJava-1-創建和銷毀對象
    可以不用在每次調用時都創建一個新的對象,可以使用預先構建好的對象,或將構建好的對象緩存起來,進行重複利用,適用於經常請求創建相同對象,並且創建對象的代價很高,如常見的單例模式寫法就是對這一點的應用;實例受控的類:能為重複的調用返回相同的類,有助於類控制某個時刻哪些實例應該存在,功能如下:1.確保它是一個Singleton或者是不可實例化的;2.使得不可變的類不會存在兩個相等的實例
  • Java內存分配和String類型的深度解析
    一、引題在java語言的所有數據類型中,String類型是比較特殊的一種類型,同時也是面試的時候經常被問到的一個知識點,本文結合java內存分配深度分析關於String的許多令人迷惑的問題。下面是本文將要涉及到的一些問題,如果讀者對這些問題都了如指掌,則可忽略此文。1、java內存具體指哪塊內存?這塊內存區域為什麼要進行劃分?是如何劃分的?
  • JVM內存分析,程式設計師進階的必經之路
    ①stack棧常說的棧內存,其實嚴格上來說應該叫Java虛擬機棧。利用開發工具IDEA斷點調試,我們可以很清晰地看到方法進棧出棧的過程。的(new 對象 new 數組,看到new這個關鍵字就可以想到堆)堆在Java虛擬機啟動時創建。
  • Java 的類加載過程
    加載階段(Loading),它是 Java 將字節碼數據從不同的數據源讀取到 JVM 中,並映射為 JVM 認可的數據結構(就是在內存中生成一個java.lang.Class對象)。只有通過了文件格式驗證之後, 字節流才會進入內存的方法區中進行存儲         2,元數據驗證,確保Class的語義描述符合Java的Class規範。如:該Class是否有父類、是否錯誤繼承了final類、是否一個合法的抽象類等。         3,字節碼驗證,通過分析數據流和控制流,確保程序語義符合邏輯。如:驗證類型轉換是合法的。
  • 談談Java內存管理
    但是在寫程序的過程中卻也往往因為這樣而造成了一些不容易察覺到的內存問題,並且在內存問題出現的時候,也不能很快的定位並解決。因此,了解並掌握Java的內存管理是一個合格的Java程式設計師必需的技能,也只有這樣才能寫出更好的程序,更好地優化程序的性能。
  • java類加載的過程概述
    加載:是將class文件讀入內存,並為之創建一個Class對象。任何類被使用時系統都會建立一個Class對象。2. 連接:(1)驗證是否有正確的內部結構,並和其他類協調一致。(2)準備負責為類的靜態成員分配內存,並設置默認初始化值。(3)解析將類的二進位數據中的符號引用替換為直接。
  • JVM內存區域之線程私有區域
    概述:對於從事C、C++開發的程式設計師來說,在內存管理領域,他們既是擁有最高權力的「皇帝」,又是從事最基礎工作的勞動人民——既擁有每個對象的「所有權」,又擔負著每一個對象從開始到終結的維護職責。對於java程式設計師來說,在虛擬機自動內存管理機制的幫助下,不再需要為沒一個new操作去配對的free/delete(C、C++語言對對象的刪除和內存釋放操作),不容易出現內存洩漏和內存溢出問題,看起來由虛擬機管理內存一切看起來很美好。
  • 如何在javascript中創建一個對象?
    javascript是一門基於對象而不是面向對象的語言,由於它的這個缺陷,在javascript中實現面向對象時十分彆扭,就比如創建對象,由於在ES6之前沒有class關鍵字,想要創建對象必須依賴以下幾種間接方式。
  • Java程式設計師必備基礎:Java代碼是怎麼運行的?
    運行時創建對象 方法調用,執行引擎解釋為機器碼 CPU執行指令 多線程切換上下文 編譯 我們都知道,java代碼是運行在Java虛擬機上的。
  • JVM中的五大內存區域劃分詳解及快速掃盲
    運行時數據區域組成虛擬機在執行java程序時,會將自己管理的內存劃分為幾個區域,每個區域都有自己的用途,並且創建時間和銷毀時間也不一樣。在程序運行時的內存區域主要可以劃分為五個,分別是:方法區、堆、虛擬機棧、本地方法棧、程序計數器。可以用下面的圖來描述:2.
  • 面試官:談談對象的創建及存儲結構
    java有兩種對象:實例對象和Class對象,實例對象就通過Class對象來創建的,類是對象抽象和集合的話,Class類就是對類的抽象和集合,第一次創建對象時,Class動態加載到JVM中的(懶加載) 對象的創建 當虛擬機遇到字節碼new指令時,檢查指令的參數能否在常量池定位到符號引用
  • 面試官:小夥子先來說一下可能引起Java內存洩露的場景吧
    本文分析一下可能引起java內存洩露的場景:通過 finalize() 方法終結器finalizers的使用是潛在內存洩漏問題的另一個來源。每當類的 finalize() 方法被重寫時,該類的對象不會立即被垃圾回收。相反,GC將它們排隊等待最後確定,這將在稍後的時間點發生。
  • 關於Java中Runtime.class.getClass()的細節分析
    先科普幾個JVM相關的基礎知識,讓大家有個整體概念,其他的內容如果在後續分析過程中遇到了再穿插介紹。其中,線程共享部分隨JVM啟動而創建,線程私有部分隨線程創建而創建。連結,是指解析Class中的符號引用,並轉換為運行時狀態的過程初始化,是指執行Class的<cinit>方法的過程在這個階段中,可以為Class創建一個新的java/lang/Class的Object,在其中定義一個欄位中存放當前Class的引用,並將這個Object的引用放入Class中作為其類對象 (非JVM規範,由實現方自行決定) ,而這個所謂的類對象
  • 「JAVA」萬字長篇詳述字節碼對象與反射機制完成動態編程
    進程,在這個JVM進程中,會保存有該JVM創建的所有線程、變量、對象,這些線程、變量、對象會共享該JVM的內存區域。1.類的加載類的加載是指將類的class文件(字節碼文件)載入JVM內存中,並為之創建一個java.lang.Class對象,也就是字節碼對象。類的加載過程由類加載器(ClassLoader)完成,類加載器由JVM提供,我們稱之為系統類加載器,同時,我們也可以繼承ClassLoader類來提供自定義類加載器。
  • 用Java創建對象的5種不同方式
    作為Java開發人員,我們通常每天創建許多對象,但是我們始終使用依賴管理系統(例如Spring)來創建這些對象。但是,還有更多創建對象的方法,我們將在本文中進行研究。用Java創建對象的共有五種方法,下面將通過示例說明這些方法,然後介紹創建對象的行的字節碼。
  • java開發工程師 javascript面向對象的初識
    new people()說明了創建了一個people類的實例對象,創建了以後,我們就可以通過」.」的方式來調用到people對象裡面的屬性和方法,所以,我們後面跟了一個.sleep(),注意,如果沒有這個括號的話,我們只是調用到了這個方法,但是缺沒有執行,執行方法必須要在後面加上一個()。隨後,我們打開html頁面,就自動的執行了people裡面的sleep方法。