主頁 > 後端開發 > JVM-類加載

JVM-類加載

2021-01-26 06:17:38 後端開發

JVM

類加載

JVM整個流程圖

一個java檔案被編譯為class檔案后,剩下的操作都交給jvm來執行,其中第一步就是將class檔案加載到jvm,而這一步就是由類加載器來完成的

類加載的流程又分為加載(Loading),驗證(Verification),準備(Preparation),決議(Resolution),初始化(Initialization)

而其中驗證,準備,決議這三步統稱為連接(Linking)

類加載器只負責加載class檔案,至于加載的class是否能正常執行則是由執行引擎決定

加載

這里的加載只是類加載器中的一步操作,也叫加載而已,這一步完成三個事情

  1. 通過一個類的全限定名來獲取定義此類的二進制位元組流
  2. 通過這個位元組流所代表的靜態存盤結構轉化為方法區的運行時資料結構
  3. 在記憶體中生成一個所代表這個類的java.long.Class物件,作為方法區這個類的各種資料的訪問入口

其中這三步在java虛擬機規范中并沒有要求特別具體,java虛擬機實作的靈活度相當大,例如第一步中獲取二進制位元組流

  • 從zip壓縮包中讀取,最終成為日后的jar,war檔案格式的基礎
  • 從網路中讀取,這種場景最經典的應用就是Web Applet
  • 運行時計算生成,這種場景最多的就是動態代理技術
  • 其他檔案生成,例如JSP應用,由JSP生成Class檔案
  • ...

相對于類加載其他階段,非陣列型別的加載階段,加載階段中是開發人員可控性最強的階段,加載階段既可以使用java虛擬機內置的引導類加載

器來完成,也可以使用用戶自定義加載器來完成

連接

驗證

驗證是連接階段的第一步,目的是為了確保Class檔案的位元組流中包含的資訊符合java虛擬機規范的全部約束要求,保證這些資訊被當做代碼后運行不會對java虛擬機產生危害

而驗證有包含四種驗證,檔案格式驗證,元資料驗證,位元組碼驗證,符號參考驗證

檔案格式驗證

第一階段要確保位元組流符合Class檔案格式規范,并能被當前版本虛擬機處理,這一階段可能包含下面這些驗證點

  • 是否以魔術 0xCAFEBABE 開頭
  • 主,次版本號是否在當前java虛擬機接收范圍內
  • 常連池的常量中是否有不被支持的常量型別(檢查常量的tag標志)
  • ....

實際上第一階段的驗證點遠遠不止這些,上面所列出的只是從HotSpot虛擬機中的一小部分,該驗證階段的主要目的就是為了保證輸入的位元組流能正確的決議并存盤在方法區中,格式上符合描述一個java型別資訊的要求.

這個階段驗證是基于二進制位元組流進行的,只有通過這個階段的驗證過后,這段位元組流才會被允許進入java虛擬機記憶體的方法區中進行存盤,后面的三個驗證階段全部都是基于方法區中的存盤結構上進行的,不會再進行直接讀取,操作位元組流了

元資料驗證

第二階段是對位元組碼描述的資訊進行語意分析,以確保其描述的資訊符合java語言的規范,這個階段可能包括驗證點如下

  • 這個類是否有父類 (除了java.lang.Object之外,所有的類都應該有父類)
  • 這個類的父類是否繼承了不允許繼承的類 (被final修飾的類)
  • 如果這個類不是抽象類,是否實作了其父類或介面之中要求實作的所有方法
  • 類中的欄位,方法是否與父類產生矛盾 (例如覆寫父類final欄位,或者出現不符合規則的方法多載,例如方法引數一致,回傳型別卻不同)
  • ...

位元組碼驗證

第三階段是整個驗證程序最復雜的一個階段,目的是通過資料類分析和控制流分析,確定程式語意是合法的,符合邏輯的,在第二階段對元資料資訊中的資料型別校驗完畢后,這個階段就是要對類的方法體(Class檔案中的Code屬性)進行校驗分析,保證被校驗類的方法在運行時不會出現對虛擬機危害的行為,例如

  • 保證任意時刻運算元堆疊的資料型別與指令代碼序列都能配合作業,例如不會出現類似于"在操作堆疊放置一個int型別資料,使用時卻按照long型別來加載到本地變數表中"這樣的情況
  • 確保任何跳轉指令都不會跳轉到方法體以外的位元組碼指令上
  • 保證方法中型別轉換總是有效的,例如把一個子類物件賦值給父類資料型別,這是安全的,但是把父類物件賦值給子類資料型別,甚至賦值給與它沒有繼承關系的完全不相干的一個資料型別,則是危險和不合法的
  • ...

如果一個型別中有方法體的位元組碼沒有通過位元組碼驗證,那么它肯定是有問題的,但是如果一個方法體通過了位元組碼驗證,也并不能保證它一定就是安全的,即使位元組碼驗證階段進行再大量,再嚴密的檢查,也不能保證這一點

符號參考驗證

最后一個階段的校驗行為發生在虛擬機將符號參考轉化為直接參考的時候,這個轉化動作將在連接的第三階段決議階段中發生,符號參考驗證可以看做是類自身以外(常連池中各種符號參考)的型別資訊進行匹配性校驗,通俗的來說就是檢查該類是否缺少或被禁止訪問它所依賴的外部某些外部類,方法,欄位等資源,本階段通常校驗下列內容

  • 符號參考中通過字串描述的全限定名是否能找到對應的類
  • 在指定類中是否存在符合方法的欄位描述符及簡單名稱描述的方法和欄位
  • 符號參考中的類,欄位,方法的可訪問性,(private,protected,public,< package >)是否可被當前類訪問
  • ...

符號參考驗證主要目的就是確保決議行為能正常運行,如果無法通過符號參考驗證,java虛擬機將拋出一個java.lang.IncompatibleCLassChangeError的子類例外

驗證階段對于虛擬機的類加載機制來說,是非常重要的,但卻不是必須執行的階段,因為驗證階段只有通過或者不通過的區別,只要通過了驗證,其后就對程式運行期沒有任何影響了,如果程式運行的全部代碼都已經被反復使用和驗證過,在生成環境的實施階段可以考慮使用-Xverify:none引數來關閉大部分的類驗證措施,以縮短虛擬機類加載的時間

準備

準備階段是正式的為類中定義的變數(即靜態變數,被static修飾的變數),分配記憶體并設定變數的初始值的階段

關于準備階段,首先是這時候進行記憶體分配僅包括類變數,而不是實體變數,實體變數將會在物件實體化時隨著物件一起分配在java堆中

在這個階段中,所有的基本型別都會被賦值為零值

? 基本資料型別的零值

資料型別 零 值
int 0
long 0L
short (short) 0
char '\u0000'
byte (byte) 0
boolean false
float 0.0f
double 0.0d
reference null

這里不包含final修飾的static,因為final在編譯期間就已經分配值了,準備階段會顯式初始化

決議

決議階段是java虛擬機將常量池內的符號參考替換為直接參考的程序

  • 符號參考(Symbolic Reference):符號參考以一組符號來描述所參考的目標,符號參考可以是任何形式的字面兩,只要使用時能無歧義的定位到目標即可,符號參考于虛擬機實作的記憶體布局無關,符號參考的目標并不一定已經被加載到虛擬機記憶體中的記憶體,符號參考的字面量形式明確定義在java虛擬機規范的Class檔案格式中
  • 直接參考(Direct Reference) 直接參考是可以直接指向目標的指標,相對偏移量,或者是一個能間接定位到目標的句柄,直接參考是和虛擬機記憶體布局相關的,同一個符號參考在不同虛擬機實體上翻譯出來的直接參考一般不會相同,如果有直接參考,那么參考的目標必須已經在虛擬機的記憶體中存在

決議動作一般針對類或介面,欄位,類方法,介面方法,方法型別等,對應常量池中的CONSTANT_Class_info,CONSTANT_Fieldref_info

CONSTANT_MethodHandle_info等

初始化

類的初始化時類加載的最后一個步驟,進行準備階段時,變數已經賦值過一次系統要求的零值,而在初始化階段,則會根據程式員通程序式編碼制定的主觀計劃去初始化類變數和其他資源,初始化階段其實就是執行類構造器clinit方法的程序,clinit并不是程式員在java代碼中撰寫的方法,它是由javac編譯器的自動產生物

clinit方法是由編譯器自動收集類中所有類變數賦值(靜態的變數)的動作和靜態陳述句塊 static{} 塊中的合并產生的,收集的順序是由陳述句在源檔案中出現的順序決定的

靜態陳述句塊中只能訪問到定義靜態陳述句塊之前的變數,而定義在它之后的變數,可以賦值,不能訪問

public class JvmDemo{
    static{
        //在定義之前可以進行賦值
        count=4;
        //但是不能進行訪問,這句代碼報錯,非法向前參考
        System.out.println(count);
    }
    private static int count=1;
}

clinit方法和類的建構式(即在虛擬機視角中的實體構造器init方法)不同,它不需要顯示呼叫父類構造器,java虛擬機會保證在呼叫子類clinit方法前,父類的clinit方法已經執行完畢,因此java虛擬機中第一個執行的clinit方法的型別一定是java.long.Object

clinit方法對于類來說并不是必須的,如果類中沒有對類變數賦值的操作,同時也沒有靜態代碼塊,那么編譯器可以不為這個類生成clinit方法

介面中不能使用靜態陳述句塊,但仍然有變數賦值的操作,因此介面與類一樣都會生成clinit方法,但與介面不同的是,執行介面clinit方法不需要先執行父介面的clinit方法,因為只有父介面中定義的變數被使用時,父介面才會被實體化,此外,介面的實作類初始化時也一樣不會執行介面中的clinit方法

java虛擬機必須保證一個類的clinit方法在多執行緒情況下能夠正確的加鎖同步,如果有多個執行緒同時初始化一個類,那么只會有其中一個執行緒執行類的clinit方法,其他執行緒都需要阻塞等待,知道活動執行緒執行完畢clinit方法

類加載器

從java虛擬機角度來看,只存在兩種類加載器器:一種啟動類加載器(Bootstrap ClassLoader),這個類加載器使用C++實作,是虛擬機的一部分,另一種就是其他所有的類加載器,這些都由java實作,獨立于虛擬機外部,并且全部繼承于java.lang.ClassLoader

而從程式角度來劃分了類加載器

啟動類加載器(引導類加載器,Bootstrap ClassLoader)

使用C++實作,嵌套在JVM內部,這個類加載器用來負責加載存放在<JAVA_HOME>\lib目錄,用于加載JVM自身需要的類,并不繼承java.lang.ClassLoader,沒有父加載器,加載應用類加載器和擴展類加載器,并指定為它們的父類加載器

擴展類加載器(Extension ClassLoader)

這個類加載器在類sun.misc.Launcher$ExtClassLoader中以java代碼實作,它負責加載<JAVA_HOME>\lib\ext目錄中,或被java.ext.dirs系統變數指定的路徑中所有類別庫

程式類加載器(系統類加載器 Application ClassLoader)

這個類加載器由sun.misc.Launcher$AppClassLoader實作,負責加載用戶類路徑ClassPath上所有類別庫,父類加載器為擴展類加載器,該類是程式中的默認加載器,一般情況java應用的類都是由它來完成,通過ClassLoader#getSystemClassLoader()方法可以獲取到該類加載器

繼承關系

ClassLoader

常用方法

  1. Class loadClass(String name) :name引數指定類裝載器需要裝載類的名字,必須使用全限定類名,如:com.smart.bean.Car,該方法有一個多載方法 loadClass(String name,boolean resolve),resolve引數告訴類裝載器時候需要決議該類,在初始化之前,因考慮進行類決議的作業,但并不是所有的類都需要決議,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要進行決議,
  2. Class defineClass(String name,byte[] b,int len):將類檔案的位元組陣列轉換成JVM內部的java.lang.Class物件,位元組陣列可以從本地檔案系統、遠程網路獲取,引數name為位元組陣列對應的全限定類名,
  3. Class findSystemClass(String name):從本地檔案系統在來Class檔案,如果本地系統不存在該Class檔案,則拋出ClassNotFoundException例外,該方法是JVM默認使用的裝載機制
  4. Class findLoadedClass(String name):呼叫該方法來查看ClassLoader是否已載入某個類,如果已載入,那么回傳java.lang.Class物件;否則回傳null,如果強行裝載某個已存在的類,那么則拋出鏈接錯誤,
  5. ClassLoader getParent():獲取類裝載器的父裝載器,除根裝載器外,所有的類裝載器都有且僅有一個父裝載器,ExtClassLoader的父裝載器是根裝載器,因為根裝載器非java語言撰寫,所以無法獲取,將回傳null,

雙親委派機制

雙親委派模型的作業程序:當接收到類加載請求時,類加載器首先會委托父類加載器(注意這里說的父類一般不是繼承關系,而是通常使用組合關系來復用父類加載器的代碼)進行加載,每一層都是如此,因此所有類加載請求最后都會到達啟動類加載器(Bootstrap ClassLoader),只有父類加載器無法完成這個加載請求時(在它的搜索范圍內沒有找到所需的類),子類加載器才會嘗試自己去完成加載

使用雙親委派模型來組織類加載器之間的關系,一個顯而易見的好處就是java中的類隨著它的類加載器一起具備了一種帶有優先級的層次關系

例如java.lang.Object,它存放在rt.jar中,無論哪一個類加載器要加載這個類,最終委托還是派給模型中處于頂端的類加載器進行加載,因此Object類在程式的各種類加載器環境中都能保證是同一個類,同時雙親委派模型也可以保證java核心API的安全性,例如自己也在專案中創建一個java.lang.String,如果加載了自定義的String類,程式將變得十分混亂

雙親委派模型實作全部集中在loadClass()方法中

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        //首先檢查請求的類是否已經被加載過了
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                //如果拋出ClassNotFound例外說明父類無法加載這個類
                //那么將呼叫當前類的findClass()來加載
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                long t1 = System.nanoTime();
                c = findClass(name);

                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

例如下面自定義一個String,全限定名為java.lang.String

package java.lang;

/**
 * @author : Jame
 * @date : 2021-01-25 13:49
 **/
public class String {
    public static void main(String[] args) {
        System.out.println("自定義String");
    }
}

運行出現下面錯誤

原因就是類加載器向上委托直到引導類加載器,引導類加載器發現可以加載java.lang下的類,于是加載了java自帶的String類,之后呼叫java自帶的String類的main方法,發現沒有該方法,于是拋出例外

其他

在JVM中判斷兩個類是否相同有兩個條件

  1. 兩個類的全限定名一致
  2. 加載這兩個類的類加載器(ClassLoader實體物件)必須一樣

即使兩個類的類物件(Class物件)來源于同一個class檔案,被同一個的類加載器加載,只要加載它們的ClassLoader物件實體不同,那么這兩個類物件也是不相等的

對類加載器的參考

JVM必須知道一個型別是由啟動加載器還是用戶類加載器加載的,如果一個型別是用戶類加載器加載的,那么JVM會將這個類加載器的一個參考型別資訊的一部分保存在方法區中,當決議一個型別到另一個型別的參考的時候,JVM需要保證這兩個型別的類加載器時相同的

類的使用和被動使用

java程式對類的使用方式分為:主動使用和被動使用

  • 主動使用,分為七種情況:

    • 創建類的實體

    • 訪問某個類或介面的靜態變數,或者對靜態變數賦值

    • 呼叫類的靜態方法

    • 反射 (例如:Class.forName("com.jame.Test"))

    • 初始化一個類的子類

    • Java虛擬機啟動時被標明為啟動的類

    • JDK7 開始提供的動態語言支持:

      java.lang.invoke.MethodHandle實體的決議結果

      REF_getStatic,REF_putStatic,REF_invokeStatic句柄對應類沒有初始化,則初始化

  • 除了以上七種情況,其他事宜Java類的方式都被看做為對類的被動使用,都不會導致類的初始化

這里的類的初始化指的是類加載中3大步驟中的最后一步初始化步驟

本文僅個人理解,如果有不對的地方歡迎評論指出或私信,謝謝?(?>?<?)?

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/252434.html

標籤:Java

上一篇:阿里Java開發手冊(嵩山版).pdf

下一篇:Spring Security 實戰干貨:AuthenticationManager的初始化細節

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more