快速理解類的加載過程

當程序主動使用某個類時,如果該類還未加載到內存中,則系統會通過如下三個步驟來對該類進行初始化:

1.加載:將class文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區的運行時數據結構,然后生成一個代表這個類的java.lang.Class對象;

2.鏈接:Java類的二進制代碼合并到JVM的運行狀態之中的過程。

(1)驗證:確保加載的類信息符合JVM規范,沒有安全方面的問題;

(2)準備:正式為類變量(static)分配內存并設置類變量默認初始值的階段,這些內存都將在方法區中進行分配;(static是在內初始化之前就完成了)

(3)解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程。()

3.初始化:

(1)執行類構造器<clinit>()方法的過程:類構造器<clinit>()方法是由編譯期自動收集類中所有類變量的賦值動作和靜態代碼塊中的語句合并產生的(類構造器是構造類信息的,不是構造該類對象的構造器);(JVM去完成的)

(2)當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化;

(3)虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步。

總結:

虛擬機把Class文件加載到內存,并對數據進行校驗、轉換解析和初始化,形成可以虛擬機直接使用的Java類型,即java.lang.Class,如下圖:

接下來會對該三個步驟逐一進行解釋。

一.類的加載

作用:查找和導入Class文件。

步驟:

1.通過一個類的全限定名獲取定義此類的二進制字節流;(那么這個時候需要一個尋找器,來尋找獲取我們的二進制字節流,而java中恰好有這么一段代碼模塊,可以實現通過類全名來獲取此類的二進制字節流這個動作,并且將這個動作放到放到java虛擬機外部去實現,以便讓應用程序決定如何獲取所需要的類,實現這個動作的代碼模塊成為“類加載器”)

2.將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構

3.在Java堆中生成一個代表這個類的java.lang.Class對象,作為對方法區中這些數據的訪問入口。(此時靜態數據結構已經放進了方法區,但是此時我們沒有辦法去進行訪問,java當中去訪問數據的方法是通過引用去操作對象,然后通過對象去操作數據,所以還需要再堆當中去生成一個代表代表這個類的java.lang.Class對象,作為方法區中的數據訪問入口)

在加載階段完成之后,這個時候在內存當中,運行時數據區的方法區以及堆就已經有數據了:

(1)方法區:類信息,靜態變量,常量;

(2)堆:代表被加載類的java.lang.Class對象。

及時編譯之后的熱點代碼并不在這個階段進入方法區。

類加載器:

類加載器作用是用來把類(class)裝載進內存的,JVM 規范定義了如下類型的類的加載器:

二.類的鏈接

1.驗證

作用:

驗證只要是為了確保Class文件中的字節流包含的信息完全符合當前虛擬機的要求,并且還要求我們的信息不會危害虛擬機自身的安全,導致虛擬機的崩潰。

驗證內容:

文件格式的驗證、元數據驗證、字節碼的驗證、符號引用的驗證。

2.準備

作用:

為類的靜態變量分配內存,并且初始化為當前類型的默認值。

解釋:

1.這里不包含用final修飾的static,因為final在編譯的時候就會分配了,準備階段會顯式初始化,這里不會為實例變量(也就是沒加static)分配初始化,類變量會分配在方法區中,而實例變量是會隨著對象一起分配到Java堆中;

2.進行分配內存的只是包括類變量(靜態變量),而不包括實例變量,實例變量是在對象實例化時隨著對象一起分配在java堆中的,通常情況下,初始值為零值,假設public static int a=1,那么a在準備階段過后的初始值為0,不為1,這時候只是開辟了內存空間,并沒有運行java代碼,a賦值為1的指令是程序被編譯后,存放于類構造器()方法之中,所以a被賦值為1是在初始化階段才會執行。

3.解析

作用:

把類中的符號引用轉換為直接引用:

(1)符號引用就是一組符號來描述目標,可以是任何字面量;

(2)直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄。

(因為再一個Class文件中,沒有辦法去表示引用關系,只能告訴你引用到10行或者20行這種,直接應用就表示你執行的位置在內存當中有一塊具體的地址,比如說a指向b是符號應用,a指向b所在的位置0x01,在內存中這叫直接引用)

解釋:

(1)解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程,解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用限定符7類符號引用進行;

(2)直接應用是與虛擬機內存布局相關的,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般是不相同的,如果有了直接引用,那引用的目標必定存在內存中;

(3)類緩存:標準的JavaSE類加載器可以按要求查找類,但一旦某個類被加載到類加載器中,它將維持加載(緩存)一段時間。不過VM垃圾回收機制可以回收這些Class對象。(同一符號引用進行多次解析請求是很常見的,除invokedynanic指令以外,虛擬機實現可以對第一次解析結果進行緩存,來避免解析動作重復進行,無論是否真正執行了多解析動作,虛擬機需要保證的是在同一個實體中,如果一個引用符號之前已經被成功解析過,那么后續的引用能析請求就應當一直成功,同樣的,如果第一次解析失敗,那么其他指令對這個符號的解析請求也應該收到相同的異常)

(4)inDy(invokedynamic)是java7引入的一條新的虛擬機指令,這是自 1.0 以來第一次引入新的虛擬機指令,到了 java 8 這條指令才第一次在java 應用,用在 lambda 表達式中,indy 與其他 invoke 指令不同的是它允許由應用級的代碼來決定方法解析。

三.類的初始化

作用:

初始化階段是執行類構造器Clinit()方法的過程,或者講得通俗易懂些,加載和鏈接以外的工作都需要在初始化中去完成。

在準備階段,類變量已賦過一次系統要求的初始值,而在初始化階段,則是根據自己通過程序制定的主觀計劃去初始化變量和其他資源,比如賦值。

什么時候發生類初始化:

1.類的主動引用(一定會發生類的初始化):

(1)當虛擬機啟動,先初始化main方法所在的類;

(2)new一個類的對象;

(3)調用類的靜態成員(除了final常量)和靜態方法;

(4)使用java.lang.reflect包的方法對類進行反射調用;

(5)當初始化一個類,如果其父類沒有被初始化,則先會初始化它的父類;

2.類的被動引用(不會發生類的初始化):

(1)當訪問一個靜態域時,只有真正聲明這個域的類才會被初始化。如:當通過子類引用父類的靜態變量不會導致子類初始化;

(2)通過數組定義類引用,不會觸發此類的初始化;

(3)引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)。

在Java中對類變量進行初始值設定有兩種方式:

1.聲明類變量是指定初始值;

2.使用靜態代碼塊為類變量指定初始值。

按照程序邏輯,必須把靜態變量定義在靜態代碼塊的前面,因為兩個的執行是會根據代碼編寫的順序來決定的,順序搞錯了可能會影響你的業務代碼。

JVM初始化步驟:

1.假如這個類還沒有被加載和連接,則程序先加載并鏈接該類;

2.假如該類的直接父類還沒有被初始化,則先初始化其直接父類;

3.假如類中有初始化語句,則系統依次執行這些初始化語句。

四.舉例

代碼如下:

public class Test {public static void main(String[] args) {A a = new A();System.out.println(A.m);/*1.加載到內存 ,會產生一個類對應class對象2.鏈接 ,鏈接結束后 m = 03.初始化 m= 100:<clinit>(){System.out.println("A類靜態代碼塊初始化”);m = 300;m=100}*/}
}class A {static {System.out.println("A類靜態代碼塊初始化");m = 300;}static int m = 100;public A() {System.out.println("A類的無參構造初始化");}
}

輸出結果:

A類靜態代碼塊初始化
A類的無參構造初始化
100

?大概流程如下:

首先在方法區產生了一些該類的靜態數據,然后在加載的類的時候,就產生了對應的class,然后看main()方法,main()方法后就開始鏈接,此時m有一個初始值0,等鏈接完沒有問題的時候,就開始執行代碼,此時new A(),就產生了一個A類的對象,這個對象就會去找到自己的Class類(指向),通過A類的數據結構,給A類對象賦值(拿到數據),賦值完成后,通過clinit()方法初始化數據,得到m等于100。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/63141.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/63141.shtml
英文地址,請注明出處:http://en.pswp.cn/web/63141.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

搭建 Elasticsearch 集群:完整教程

本文將詳細介紹如何在 Linux 環境下搭建一個 Elasticsearch 集群&#xff0c;涵蓋環境準備、配置優化、服務啟動等多個環節。 一、環境準備 創建安裝目錄 mkdir /es cd /es解壓 Elasticsearch 安裝包 tar -xzf elasticsearch-7.10.1-linux-x86_64.tar.gz -C /es配置環境變量 編…

寶塔-docker拉取寶塔鏡像,并運行寶塔鏡像

寶塔-拉取寶塔鏡像&#xff0c;并運行鏡像 第1步&#xff1a;查詢 docker search btpanel/baota此docker鏡像由堡塔安全官方發布&#xff0c;鏡像版本為寶塔面板9.2.0正式版和9.0.0_lts 穩定版&#xff0c;鏡像會隨著寶塔面板更新。 目前支持x86_64和arm架構可供下載使用 版本…

使用 Valgrind 檢測 C 程序中的內存問題 -基礎教程

內存泄漏是許多 C 語言程序中的常見問題&#xff0c;它不僅會導致程序性能下降&#xff0c;甚至可能讓系統崩潰。為了檢測和修復這些問題&#xff0c;Valgrind 是一個非常強大的工具&#xff0c;它可以幫助我們分析 C 程序中的內存使用情況&#xff0c;檢測內存泄漏、越界訪問、…

窮舉vs暴搜vs深搜vs回溯vs剪枝專題一>子集

題目&#xff1a; 兩個方法本質就是決策樹的畫法不同 方法一解析&#xff1a; 代碼&#xff1a; class Solution {private List<List<Integer>> ret;//返回結果private List<Integer> path;//記錄路徑&#xff0c;注意返回現場public List<List<Int…

leecode雙指針部分題目

leecode雙指針部分題目 1. 驗證回文串2. 判斷子序列3. 兩數之和 II - 輸入有序數組4. 盛最多水的容器5. 三數之和 1. 驗證回文串 如果在將所有大寫字符轉換為小寫字符、并移除所有非字母數字字符之后&#xff0c;短語正著讀和反著讀都一樣。則可以認為該短語是一個 回文串 。 …

Web 應用如何使用sqlite?使用 sql.js 實現前端 SQLite 數據庫操作

前言 在 Web 應用開發中&#xff0c;前端數據處理的重要性日益增加。為了實現更高效的前端數據管理&#xff0c;特別是在處理結構化數據時&#xff0c;sql.js 提供了一個出色的解決方案。sql.js 是將 SQLite 數據庫編譯為 JavaScript 的庫&#xff0c;允許開發者在瀏覽器環境中…

docker 安裝 mysql8.0容器外無法連接

文章目錄 概要問題描述解決方案其他命令 概要 主要是mysql5.7和mysql8.0的兼容性問題。 排查了很久 其實就是配置文件的一句話的事情 感覺mysql8.0更為嚴謹 這樣可能是考慮杜絕一些漏洞吧 問題描述 在容器內 netstat -an | grep 3306 都不行 在容器外 netstat -an | grep 2…

TCP協議簡單分析和握手揮手過程

TCP介紹 TCP是可靠的傳輸層協議&#xff0c;建立連接之前會經歷3次握手的階段。 確認機制&#xff1a;接受方 收到數據之后會向 發送方 回復ACK重傳機制&#xff1a;發送方 在一定時間內沒有收到 接收方的ACK就會重新發送 握手目的&#xff1a;與端口建立連接 TCP的三次握手 …

VisualStudio vsix插件自動加載

本文介紹如何在Visual Studio擴展中實現PackageRegistration&#xff0c;包括設置UseManagedResourcesOnly為true&#xff0c;允許背景加載&#xff0c;并針對C#、VB、F#項目提供自動裝載&#xff0c;附官方文檔鏈接。增加以下特性即可…… [PackageRegistration(UseManagedRe…

opencv所有常見函數

一、opencv圖像操作 二、opencv圖像的數值運算 三、opencv圖像的放射變換 四、opencv空間域圖像濾波 五、圖像灰度化與直方圖 六、形態學圖像處理 七、閾值處理與邊緣檢測 八、輪廓和模式匹配

【Excel】單元格分列

目錄 分列&#xff08;新手友好&#xff09; 1. 選中需要分列的單元格后&#xff0c;選擇 【數據】選項卡下的【分列】功能。 2. 按照分列向導提示選擇適合的分列方式。 3. 分好就是這個樣子 智能分列&#xff08;進階&#xff09; 高級分列 Tips&#xff1a; 新手推薦基…

【STM32練習】基于STM32的PM2.5環境監測系統

一.項目背景 最近為了完成老師交付的任務&#xff0c;遂重制了一下小項目用STM32做一個小型的環境監測系統。 項目整體示意框圖如下&#xff1a; 二.器件選擇 單片機&#xff08;STM32F103&#xff09;數字溫濕度模塊&#xff08;DHT11&#xff09;液晶顯示模塊&#xff08;0.8…

《開源數據:開啟信息共享與創新的寶藏之門》

《開源數據&#xff1a;開啟信息共享與創新的寶藏之門》 一、開源數據概述&#xff08;一&#xff09;開源數據的定義&#xff08;二&#xff09;開源數據的發展歷程 二、開源數據的優勢&#xff08;一&#xff09;成本效益優勢&#xff08;二&#xff09;靈活性與可定制性&…

ReactPress最佳實踐—搭建導航網站實戰

Github項目地址&#xff1a;https://github.com/fecommunity/easy-blog 歡迎Star。 近期&#xff0c;阮一峰在科技愛好者周刊第 325 期中推薦了一款開源工具——ReactPress&#xff0c;ReactPress一個基于 Next.js 的博客和 CMS 系統&#xff0c;可查看 demo站點。&#xff08;…

2024,大模型殺進“決賽圈”

Henry Chesbrough在著作《通過技術創新盈利勢在必行》中&#xff0c;曾提出過一個創新的“漏斗模型”。開放式創新一開始鼓勵百花齊放&#xff0c;但最終只有10%的技術能夠通過這個漏斗&#xff0c;成功抵達目標市場target market&#xff0c;進入到商業化與產業化的下一個階段…

STM8單片機學習筆記·GPIO的片上外設寄存器

目錄 前言 IC基本定義 三極管基礎知識 單片機引腳電路作用 STM8GPIO工作模式 GPIO外設寄存器 寄存器含義用法 CR1&#xff1a;Control Register 1 CR2&#xff1a;Control Register 2 ODR&#xff1a;Output Data Register IDR&#xff1a;Input Data Register 賦值…

頁面加載速度優化策略:提升用戶體驗的關鍵

文章目錄 前言一、為什么需要優化頁面加載速度&#xff1f;二、前端優化技術三、后端優化策略四、構建與部署優化五、案例研究&#xff1a;實際效果展示結語 前言 在當今快節奏的互聯網環境中&#xff0c;頁面加載速度不僅是用戶體驗的重要組成部分&#xff0c;更是影響網站性…

【CSS in Depth 2 精譯_081】 13.1:CSS 漸變效果(下)——CSS 徑向漸變(13.1.3)+ CSS 錐形漸變(13.1.4)

當前內容所在位置&#xff08;可進入專欄查看其他譯好的章節內容&#xff09; 第四部分 視覺增強技術 ??【第 13 章 漸變、陰影與混合模式】 ?? 13.1 漸變 ?? 13.1.1 使用多個顏色節點&#xff08;上&#xff09;13.1.2 顏色插值方法&#xff08;中&#xff09;13.1.3 徑…

商務禮儀學習筆記

時間,場合,地點 女士: 1. 著裝(裙裝套裝,最短不能超過膝蓋一拳,裙子形狀直通,顏色簡單不能花里胡哨,上下顏色不能超過三種,深灰深藍;上下顏色,裝飾,面料統一;絲襪不要過于花,肉色透明比較推薦) 2. 妝容和發型(經過搭理,不要毛躁; 膚色保持一致,均衡;腮紅…

ubuntu 用 ss-tproxy的最終網絡結構

1、包含了AD廣告域名篩選 2、Ss-tproxy 國內國外地址分類 3、chinadns-ng解析 4、透明網關 更多細節看之前博客 ubuntu 用ss-TPROXY實現透明代理&#xff0c;基于TPROXY的透明TCP/UDP代理,在 Linux 2.6.28 后進入官方內核。ubuntu 用 ss-tproxy的內置 DNS 前掛上 AdGuardHome…