JVM 虛擬機&&類載入(一)

来源:https://www.cnblogs.com/burg-xun/archive/2020/04/13/12689079.html
-Advertisement-
Play Games

虛擬機 虛擬機簡介 Java 虛擬機(JVM)是運行java程式的抽象電腦,它是電腦設備的規範,可以採用不同方式進行實現,java 程式通過運行在JVM中實現跨平臺,一次編譯到處運行,不同的操作系統有不同的JDK版本,通過調用JNI方法去實現調用不同操作系統的方法 Java虛擬機不和包括java ...


虛擬機

虛擬機簡介

Java 虛擬機(JVM)是運行java程式的抽象電腦,它是電腦設備的規範,可以採用不同方式進行實現,java 程式通過運行在JVM中實現跨平臺,一次編譯到處運行,不同的操作系統有不同的JDK版本,通過調用JNI方法去實現調用不同操作系統的方法

graph LR A[java源文件] B[class二進位位元組碼] C[JVM] D((Java native介面)) F[windows本地方法] H[linux本地方法] I[macOS本地方法] A-.編譯.->B B-.載入.->C C-.打開文件.->D D-.windows-JDK.->F D-.linux-JDK.->H D-.macOS-JDK.->I

Java虛擬機不和包括java的語言綁定,它只和Class文件這種特定的二進位文件格式綁定,class文件中包含了虛擬機指令集和符號表以及若幹其他輔信息。

graph LR A[java程式] B[JRuby程式] C[Groovy程式] D[java編譯器] E[JRuby編譯器] F[Groovy編譯器] H[位元組碼] I[Java虛擬機] A-.*.java文件.->D B-.*.rb文件.->E C-.*.groovy文件.->F D-->H E-->H F-->H H-.*.class文件.->I

虛擬機產品

  1. Sun HotSpot(JVM)
  2. BEA JRocket
  3. IBM J9
  4. Microsoft JVM
  5. Google Android Dalvik

Class 文件

Class文件是以8位位元組基礎單位的二進位流,各項數據嚴格按照順序排列在class文件中,中間沒有任何分隔符,如果超過8位,是按照高位在前的方式存儲的。

Class文件的組成

  1. java虛擬機指令集
  2. 符號表
  3. 其他輔助信息

常量池

java代碼在編譯後,Class文件並表不會保存各個方法,欄位在記憶體裡面的最終佈局,因為在編譯期間並不知道引用類的實際地址,因此只能使用符號描述來代替,當虛擬機真正的運行的時候,需要從class文件的常量池獲得對應的符號引用,然後在類的創建或者運行時解析稱具體的記憶體地址

也就是說在類被載入的時候,一定是在記憶體中分配了具體的地址,這樣在解析的時候就可以替換符號引用成真正的地址。

Class文件的常量池主要存放2大類型:

  • 字面量 字面量是接近java語言層面的常量概念,如文本字元串,聲明final的常量值等
  • 符號引用 主要包括類和介面的全稱限定名,欄位的名稱和描述符,方法的名稱和描述符

虛擬機類載入

類載入到JVM中生命空間

graph LR A[載入Loading] B[驗證Verification] C[準備Preparation] D[解析Resolution] E[初始化Initialization] F[使用Using] G[卸載Unloading] A-->B D-->E E-->F F-->G subgraph 連接 B-->C C-->D end

載入

載入的過程做的事情是:

  • 獲取二進位位元組流
  • 靜態存儲結構轉化為方法區的運行時結數據結構
  • 在java堆對象裡面生成一個類對象,作為方法區的入口

Java中獲取二進位位元組流的途徑有

  • 從Zip,Jar,War等格式文件中獲取
  • 從網路中獲取 Applet應用
  • 運行時計算生成,動態代理技術
  • JSP應用生成對應的Class類

驗證

驗證是連接的第一步,要確保Class文件中的位元組流包含的信息符合JVM的要求,驗證階段大體分為如下幾個階段

  • 文件格式驗證 驗證文件標識是否正確,版本號是否能匹配當前JVM,常量池中的常量是否有不支持的類型等 。經過這個階段的驗證後,位元組流才會進入的方法區中進行存儲,後面的驗證只是基於方法區的存儲結構驗證,不會在驗證位元組流
  • 元數據驗證 主要是對位元組碼描述的語義進行分析,保證符號java語言的規範,類的繼承,抽象類的實現等等
  • 位元組碼驗證
  • 符號引用驗證 該階段發生在符號引用替換成直接引用時,驗證符號引用的匹配校驗等等

準備

準備階段是為了給類分配記憶體並設置類變數的初始化Clint,這些變數使用的記憶體,都在方法區中進行分配,只是對類變數進行記憶體分配(Static 修飾的)不包括實例變數,實例變數是隨著類實例化後一起在堆中分配的

:類的初始化 父靜態變數 父類靜態塊 子類靜態變數 子類的靜態塊

: 對象的初始化 父類的變數初始化,父類的構造函數。。。。。。

解析

解析的目的是將常量池中的符號引用替換為直接引用

欄位的解析

class A extends B implements C{
    private String str; //欄位的解析
}
graph TB A{A是否能匹配} B{B是否能匹配} C{C是否能匹配} A-->|是|E[結束] A-->|否|C subgraph 介面 C-->|是|F(結束) end subgraph 父類 C-->|否|B B-->|是|I(結束) B-->|否|J((拋出異常)) end

類方法解析

  • 先查找本類
  • 然後父類中遞歸查找
  • 在類實現的介面列表及它們的父介面

介面方法解析

  • 先查找本介面
  • 在介面的父介面中遞歸查找

類載入器

通過類的全限定名來獲取描述類的二進位位元組流,把類載入這個階段中的動作放到虛擬機外部去實現,以便應用程式能夠自己決定如果去獲取自己需要的類,實現這個動作加做類載入器。
Java中有abstract class ClassLoader 類。
ClassLoader.loadClass類的核心總結是:

  • 同一個時間只能允許一個線程去載入類
  • 在載入類之前會檢查下是否已經載入過,只有沒有被載入的才能去被載入
  • 能父載入器載入的絕不會交給子載入器去載入,為什麼要這樣,是為了保證安全,比如你自己寫個相同名字的java底層類比如java.util.ArrayList通過子載入器到記憶體中,那不是亂套了麽,到時候植入個病毒代碼,用的人 不GG了
  • 父載入器載入不到的才會交給子載入器載入

載入器類型

從JVM的角度去看 只存在2中類載入器,一種是啟動類載入器(Bootstrap ClassLoader)是由C++語言實現的。還有一種是另外的類載入器,是由java語言實現的,獨立於JVM,全部是繼承了java.lang.ClassLoader

如果從我們開發角度去看,分為3中類載入器:

  • Bootstrap ClassLoader啟動類載入器,負責Java_Home/lib 下麵的類庫載入到記憶體中,由於這邊涉及到虛擬機本地的實現細節,所有我們開發者無法獲取到類載入器的引用。
  • Extension ClassLoader 擴展類載入器 負責載入Java_Home/lib/ext或者由系統變數java.ext.dirs指定位置的類載入到記憶體中
//Launcher.class文件中
static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;

        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) {
                    if (instance == null) {
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
        }
     ....        
}
  • Application ClassLoader 應用程式類雞載入器,它負責系統路徑ClassPath中指定的類庫載入到記憶體中,這個類載入器是
    ClassLoader類中的getSystemClassLoader靜態方法的返回值
 //java/lang/ClassLoader.java 文件中
 public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
graph BT A[Bootstrap載入器] B[ExtClassLoader] C[AppClassLoader] D[UseClassLoader1] F[UseClassLoader2] B --> A subgraph 非JVM C --> B D --> C F --> C end

有興趣的可以自己執行看下

String appClassLoaderPath = System.getProperty("java.class.path");
System.out.println(appClassLoaderPath);
String extClassLoaderPath = System.getProperty("java.ext.dirs");
System.out.println(extClassLoaderPath);
String bootClassLoaderPath = System.getProperty("sun.boot.class.path");
System.out.println(bootClassLoaderPath);

雙親委派

什麼是雙親委派

  • 如果一個類載入器收到了某個類的載入請求,則該載入器不會載入,而是把這個請求拋給父類載入器,因此所有的類載入請求都會到達頂端的類載入器
  • 只有當父類載入器在其範圍內沒法找到所需要類,才會把結果反饋給子載入器,子載入器在嘗試自己載入

如何打斷

自己寫個類載入器 並重寫loadClass方法 就可以了!

為什麼要使用雙親委派

  • 對任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。判斷兩個類是否"相等",必須是在這兩個類被同一個類載入器載入的前提下。
  • 基於雙親委派模型設計,那麼Java中基礎類,如Object類重覆多次的問題就不會存在了,因為經過層層傳遞,載入請求最終都會被BootstrapClassLoader所響應。載入的Object類也會只有一個,否則如果用戶自己編寫了一個java.lang.Object類,並把它放到了ClassPath中,會出現很多個Object類,這樣Java類型體系中最最基礎的行為都無法保證,應用程式也將一片混亂。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • src/Test.js import React from 'react'; function Test() { return ( <div> test </div> ); } export default Test; src/index.js中引入組件Test 頁面中效果 ...
  • react fiber 指react 16以上的版本 引入react的方式: 1、引入.js文件 2、使用腳手架工具(推薦) 推薦使用react官方提供的腳手架工具:create-react-app React開發環境準備 (npx 是 npm 的高級版本,npx 具有更強大的功能) npx cre ...
  • JavaScript 中 apply、call、bind方法的異同: 相同點 都是用來動態指定函數 this 對象的指向 第一個參數都是 this 要指向的對象,也就是要指定的上下文 都可以利用後續參數傳參 不同點 傳參形式不同:apply 方法接受的是一個參數數組,call 和 bind 方法接受 ...
  • TypeScript聯合類型 聯合類型表示取值可以為多種類型中的一種 如下所示 這一塊我們必須使用string或者number都支持的類型,那麼下麵我們可以進行調用擴展方法toString() TypeScript中對象類型 介面 介面可以描述一種抽象的行為,也可以描述對象的結構形狀,當然我們也需要 ...
  • v-bind:class=" " 綁定樣式 <div id="app"> <!-- 值是對象形式,欄位名是class樣式名,值是boolean值,true是引用該樣式,false不引用 --> <!-- 值是false,只是不引用該樣式,並不是就不顯示該元素了 --> <p v-bind:class ...
  • 前臺:支持(5+3[時尚單頁風格])八套模版,可以在後臺切換 業務模塊(首頁管理) 1. 網站信息:維護網站基本信息,比如標題、描述、關鍵詞、聯繫方式、地址等 2. 業務說明:網站首頁文字業務介紹 3. 公司理念:網站首頁展示公司的4個理念 4. 輪播圖片:網站首頁上面4個輪播圖5. 項目案例:網站 ...
  • 在前面的博客中已經介紹過如何使用Python來操作MySQL資料庫,最近需要將一批數據從csv文件中遷移到Oracle資料庫中,也打算用Python來實現,趁著這個機會,也寫一篇博客學習總結一些如何使用Python來操作Oracle資料庫。 ...
  • 原文鏈接:http://www.yiidian.com/fastjson/fastjson json javabean.html 1 簡單JSON與JavaBean的轉換 1.1 設計Student實體類 1.2 簡單JSON轉為JavaBean MainApp: 運行效果為: 1.3 JavaBe ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...