Java中對象的創(chuàng)建和銷毀過程詳析
前言
在 Java 編程里,對象的創(chuàng)建和銷毀是基礎且關鍵的操作,深刻理解這一過程有助于編寫出高效、穩(wěn)定的代碼。下面將詳細闡述 Java 中對象的創(chuàng)建和銷毀過程。
對象的創(chuàng)建過程
1. 類加載檢查
當代碼中使用 new 關鍵字創(chuàng)建對象時,Java 虛擬機(JVM)首先會檢查該對象對應的類是否已經(jīng)被加載到內(nèi)存中。如果尚未加載,JVM 會通過類加載器將該類的字節(jié)碼文件加載到內(nèi)存,并對其進行驗證、準備和解析等操作,最終完成類的初始化。例如,當執(zhí)行 Person person = new Person(); 時,JVM 會先確認 Person 類是否已加載。
2. 分配內(nèi)存
類加載完成后,JVM 會為新對象分配內(nèi)存空間。分配內(nèi)存的方式主要有兩種:
- 指針碰撞:假設 Java 堆中的內(nèi)存是規(guī)整的,所有用過的內(nèi)存放在一邊,空閑的內(nèi)存放在另一邊,中間放著一個指針作為分界點的指示器,那所分配內(nèi)存就僅僅是把那個指針向空閑空間那邊挪動一段與對象大小相等的距離,這種分配方式稱為“指針碰撞”(Bump the Pointer)。
- 空閑列表:如果 Java 堆中的內(nèi)存并不是規(guī)整的,已使用的內(nèi)存和空閑的內(nèi)存相互交錯,那就沒有辦法簡單地進行指針碰撞了,虛擬機就必須維護一個列表,記錄上哪些內(nèi)存塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,并更新列表上的記錄,這種分配方式稱為“空閑列表”(Free List)。
選擇哪種分配方式由 Java 堆是否規(guī)整決定,而 Java 堆是否規(guī)整又由所采用的垃圾收集器是否帶有空間壓縮整理(Compact)的能力決定。
3. 初始化零值
內(nèi)存分配完成后,JVM 會將分配到的內(nèi)存空間都初始化為零值(不包括對象頭)。這一步操作保證了對象的實例字段在 Java 代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數(shù)據(jù)類型所對應的零值。例如,int 類型的字段初始值為 0,boolean 類型的字段初始值為 false。
4. 設置對象頭
JVM 會對對象進行必要的設置,例如這個對象是哪個類的實例、如何才能找到類的元數(shù)據(jù)信息、對象的哈希碼、對象的 GC 分代年齡等信息。這些信息存放在對象的對象頭(Object Header)之中。
5. 執(zhí)行 init 方法
在上述工作都完成之后,從 Java 程序的視角看來,對象已經(jīng)產(chǎn)生了,但從 JVM 的視角來看,對象創(chuàng)建才剛剛開始——<init> 方法還沒有執(zhí)行,所有的字段都還為零。所以一般來說(由字節(jié)碼中是否跟隨有 invokespecial 指令所決定),執(zhí)行 new 指令之后會接著執(zhí)行 <init> 方法,把對象按照程序員的意愿進行初始化,這樣一個真正可用的對象才算完全被構造出來。
對象的銷毀過程
1. 可達性分析
在 Java 中,對象的銷毀主要由垃圾回收機制(GC)負責。JVM 會通過可達性分析算法來判斷對象是否存活。該算法以一系列被稱為“GC Roots”的對象為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連時,則證明此對象是不可達的,就有可能被回收。常見的 GC Roots 包括:
- 虛擬機棧(棧幀中的本地變量表)中引用的對象。
- 方法區(qū)中類靜態(tài)屬性引用的對象。
- 方法區(qū)中常量引用的對象。
- 本地方法棧中 JNI(即一般說的 Native 方法)引用的對象。
2. 第一次標記
當對象被判定為不可達時,它會被第一次標記。但此時對象還不會被立即回收,而是會被放入一個名為 F - Queue 的隊列中,并由一個低優(yōu)先級的線程去執(zhí)行隊列中對象的 finalize() 方法(如果對象重寫了該方法)。
3. finalize() 方法執(zhí)行
finalize() 方法是對象逃脫死亡命運的最后一次機會。在該方法中,對象可以重新與引用鏈上的任何一個對象建立關聯(lián),例如把自己(this 關鍵字)賦值給某個類變量或者對象的成員變量。如果對象在 finalize() 方法中成功拯救了自己,那在第二次標記時它將被移除出“即將回收”的集合;如果對象沒有逃脫,那基本上它就真的要被回收了。不過需要注意的是,finalize() 方法的執(zhí)行是不可靠的,JVM 并不保證該方法一定會被執(zhí)行。
4. 第二次標記
如果對象在 finalize() 方法執(zhí)行后仍然沒有與 GC Roots 建立引用關系,它會被進行第二次標記。經(jīng)過第二次標記的對象,就會被真正地列入可回收對象的集合。
5. 垃圾回收
當垃圾回收器執(zhí)行垃圾回收操作時,會回收那些經(jīng)過第二次標記的對象所占用的內(nèi)存空間,將其釋放回 Java 堆中,供后續(xù)新對象的分配使用。不同的垃圾回收器采用不同的算法和策略來執(zhí)行垃圾回收,例如標記 - 清除算法、標記 - 整理算法、復制算法等。
知識補充:
在 Java 虛擬機的堆區(qū),每個對象都可能處于以下三種狀態(tài)之一。
1)可觸及狀態(tài):當一個對象被創(chuàng)建后,只要程序中還有引用變量引用它,那么它就始終處于可觸及狀態(tài)。
2)可復活狀態(tài):當程序不再有任何引用變量引用該對象時,該對象就進入可復活狀態(tài)。在這個狀態(tài)下,垃圾回收器會準備釋放它所占用的內(nèi)存,在釋放之前,會調(diào)用它及其他處于可復活狀態(tài)的對象的 finalize() 方法,這些 finalize() 方法有可能使該對象重新轉到可觸及狀態(tài)。
3)不可觸及狀態(tài):當 Java 虛擬機執(zhí)行完所有可復活對象的 finalize() 方法后,如果這些方法都沒有使該對象轉到可觸及狀態(tài),垃圾回收器才會真正回收它占用的內(nèi)存。
總結
到此這篇關于Java中對象的創(chuàng)建和銷毀過程的文章就介紹到這了,更多相關Java對象的創(chuàng)建和銷毀內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring?Boot?整合持久層之Spring Data JPA
在介紹Spring Data JPA的時候,我們首先認識下Hibernate。Hibernate是數(shù)據(jù)訪問解決技術的絕對霸主,使用O/R映射技術實現(xiàn)數(shù)據(jù)訪問,O/R映射即將領域模型類和數(shù)據(jù)庫的表進行映射,通過程序操作對象而實現(xiàn)表數(shù)據(jù)操作的能力,讓數(shù)據(jù)訪問操作無須關注數(shù)據(jù)庫相關的技術2022-08-08
解決Javaweb 提交表單到servlet時出現(xiàn)空白頁面,但網(wǎng)站不報錯問題
這篇文章主要介紹了解決Javaweb 提交表單到servlet時出現(xiàn)空白頁面,但網(wǎng)站不報錯的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
springboot+angular4前后端分離 跨域問題解決詳解
這篇文章主要介紹了springboot+angular4前后端分離 跨域問題解決詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-09-09
SpringBoot 動態(tài)配置Profile環(huán)境的方式
這篇文章主要介紹了SpringBoot 動態(tài)配置Profile環(huán)境的方式,本文通過圖文實例相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10

