日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

虚拟机的分类_「面试必备」Java虚拟机知识点复习手册(下)

發(fā)布時間:2025/3/19 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚拟机的分类_「面试必备」Java虚拟机知识点复习手册(下) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

關注我的微信公眾號:后端技術漫談

不定期推送關于后端開發(fā)、爬蟲、算法題、數(shù)據結構方面的原創(chuàng)技術文章,以及生活中的逸聞趣事。

我目前是一名后端開發(fā)工程師。主要關注后端開發(fā),數(shù)據安全,網絡爬蟲,物聯(lián)網,邊緣計算等方向。

原創(chuàng)博客主要內容

  • Java知識點復習全手冊
  • Leetcode算法題解析
  • 劍指offer算法題解析
  • SpringCloud菜鳥入門實戰(zhàn)系列
  • SpringBoot菜鳥入門實戰(zhàn)系列
  • Python爬蟲相關技術文章
  • 后端開發(fā)相關技術文章

前言

本文快速回顧了常考的的知識點,用作面試復習,事半功倍。

上篇主要內容為:虛擬機數(shù)據區(qū)域,垃圾回收

下篇主要內容為:類加載機制

面試知識點復習手冊

全復習手冊文章導航

Csdn全復習手冊文章導航:https://blog.csdn.net/qqxx6661/article/details/86775594

已發(fā)布知識點復習手冊

  • Java基礎知識點面試手冊
  • 快速梳理23種常用的設計模式
  • Redis基礎知識點面試手冊
  • Java容器(List、Set、Map)知識點快速復習手冊
  • Java并發(fā)知識點快速復習手冊(上)
  • Java并發(fā)知識點快速復習手冊(下)
  • Java虛擬機知識點快速復習手冊(上)
  • Java虛擬機知識點快速復習手冊(下)

參考

本文內容參考自CyC2018的Github倉庫:CS-Notes

https://github.com/CyC2018/CS-Notes/

有刪減,修改,補充額外增加內容

其他參考文章:

  • 微信文章:精華:Java 開發(fā)崗面試知識點解析
  • http://how2j.cn/k/j2se-interview/j2se-interview-java/624.html

本作品采用知識共享署名-非商業(yè)性使用 4.0 國際許可協(xié)議進行許可。

類加載機制

類是在運行期間動態(tài)加載的。

類的生命周期

包括以下 7 個階段:

  • 加載(Loading)
  • 驗證(Verification)
  • 準備(Preparation)
  • 解析(Resolution)
  • 初始化(Initialization)
  • 使用(Using)
  • 卸載(Unloading)

其中解析過程在某些情況下可以在初始化階段之后再開始,這是為了支持 Java 的動態(tài)綁定。

類加載過程

包含了加載、驗證、準備、解析和初始化這 5 個階段。

1. 加載

加載是類加載的一個階段,注意不要混淆。

加載過程完成以下三件事:

  • 通過一個類的全限定名來獲取定義此類的二進制字節(jié)流。
  • 將這個字節(jié)流所代表的靜態(tài)存儲結構轉化為方法區(qū)的運行時存儲結構。
  • 在內存中生成一個代表這個類的 Class 對象,作為方法區(qū)這個類的各種數(shù)據的訪問入口。

其中二進制字節(jié)流可以從以下方式中獲取:

  • 從 ZIP 包讀取,成為 JAR、EAR、WAR 格式的基礎。
  • 從網絡中獲取,最典型的應用是 Applet。
  • 運行時計算生成,例如動態(tài)代理技術,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理類的二進制字節(jié)流。
  • 由其他文件生成,例如由 JSP 文件生成對應的 Class 類。

2. 驗證

確保 Class 文件的字節(jié)流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全。

3. 準備

準備階段為static修飾的變量分配內存并設置初始值,使用的是方法區(qū)的內存。

實例變量不會在這階段分配內存,它將會在對象實例化時隨著對象一起分配在Java堆中。

初始值一般為 0 值,例如下面的類變量 value 被初始化為 0 而不是 123。

public static int value = 123;

如果類變量是常量,那么會按照表達式來進行初始化,而不是賦值為 0。

public static final int value = 123;

4. 解析

將常量池的符號引用替換為直接引用的過程。

其中解析過程在某些情況下可以在初始化階段之后再開始,這是為了支持 Java 的動態(tài)綁定

5. 初始化

初始化階段才真正開始執(zhí)行類中定義的 Java 程序代碼。初始化階段即虛擬機執(zhí)行類構造器 () 方法的過程。

在準備階段,類變量已經賦過一次系統(tǒng)要求的初始值,而在初始化階段,根據程序員通過程序制定的主觀計劃去初始化類變量和其它資源。

() 方法具有以下特點:

  • 是由編譯器自動收集類中所有類變量的賦值動作和靜態(tài)語句塊中的語句合并產生的,編譯器收集的順序由語句在源文件中出現(xiàn)的順序決定。特別注意的是,靜態(tài)語句塊只能訪問到定義在它之前的類變量,定義在它之后的類變量只能賦值,不能訪問。例如以下代碼:
public class Test { static { i = 0; // 給變量賦值可以正常編譯通過 System.out.print(i); // 這句編譯器會提示“非法向前引用” } static int i = 1;}
  • 與類的構造函數(shù)(或者說實例構造器 ())不同,不需要顯式的調用父類的構造器。虛擬機會自動保證在子類的 () 方法運行之前,父類的 () 方法已經執(zhí)行結束。因此虛擬機中第一個執(zhí)行 () 方法的類肯定為 java.lang.Object。
  • 由于父類的 () 方法先執(zhí)行,也就意味著父類中定義的靜態(tài)語句塊要優(yōu)先于子類的變量賦值操作。例如以下代碼:
static class Parent { public static int A = 1; static { A = 2; }}static class Sub extends Parent { public static int B = A;}public static void main(String[] args) { System.out.println(Sub.B); // 2}
  • () 方法對于類或接口不是必須的,如果一個類中不包含靜態(tài)語句塊,也沒有對類變量的賦值操作,編譯器可以不為該類生成 () 方法。
  • 接口中不可以使用靜態(tài)語句塊,但仍然有類變量初始化的賦值操作,因此接口與類一樣都會生成 () 方法。但接口與類不同的是,執(zhí)行接口的 () 方法不需要先執(zhí)行父接口的 () 方法。只有當父接口中定義的變量使用時,父接口才會初始化。另外,接口的實現(xiàn)類在初始化時也一樣不會執(zhí)行接口的 () 方法。
  • 虛擬機會保證一個類的 () 方法在多線程環(huán)境下被正確的加鎖和同步,如果多個線程同時初始化一個類,只會有一個線程執(zhí)行這個類的 () 方法,其它線程都會阻塞等待,直到活動線程執(zhí)行 () 方法完畢。如果在一個類的 () 方法中有耗時的操作,就可能造成多個線程阻塞,在實際過程中此種阻塞很隱蔽。

類初始化時機

主動引用

虛擬機規(guī)范中并沒有強制約束何時進行加載,但是規(guī)范嚴格規(guī)定了有且只有下列五種情況必須對類進行初始化(加載、驗證、準備都會發(fā)生):

  • 遇到 new、getstatic、putstatic、invokestatic 這四條字節(jié)碼指令時,如果類沒有進行過初始化,則必須先觸發(fā)其初始化。最常見的生成這4 條指令的場景是: new 關鍵字實例化對象; 讀取或設置一個類的靜態(tài)字段(被final修飾、已在編譯期把結果放入常量池的靜態(tài)字段除外)的時候; 調用類的靜態(tài)方法
  • 類進行反射調用的時候,如果類沒有進行初始化,則需要先觸發(fā)其初始化。
  • 當初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進行過初始化,則需要先觸發(fā)其父類的初始化。
  • 當虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類(包含 main() 方法的那個類),虛擬機會先初始化主類
  • 當使用 JDK 1.7 的動態(tài)語言支持時,如果一個 java.lang.invoke.MethodHandle 實例最后的解析結果為 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且這個方法句柄所對應的類沒有進行過初始化,則需要先觸發(fā)其初始化

被動引用

以上 5 種場景中的行為稱為對一個類進行主動引用。

除此之外,所有引用類的方式都不會觸發(fā)初始化,稱為被動引用。

被動引用的常見例子包括:

  • 通過子類引用父類的靜態(tài)字段,不會導致子類初始化
System.out.println(SubClass.value); // value 字段在 SuperClass 中定義
  • 通過數(shù)組定義來引用類,不會觸發(fā)此類的初始化。該過程會對數(shù)組類進行初始化,數(shù)組類是一個由虛擬機自動生成的、直接繼承自 Object 的子類,其中包含了數(shù)組的屬性和方法。
SuperClass[] sca = new SuperClass[10];
  • 使用常量:常量在編譯階段會存入調用類的常量池中,本質上并沒有直接引用到定義常量的類,因此不會觸發(fā)定義常量的類的初始化。
System.out.println(ConstClass.HELLOWORLD);

類加載器

實現(xiàn)類的加載動作。在 Java 虛擬機外部實現(xiàn),以便讓應用程序自己決定如何去獲取所需要的類。

1. 類與類加載器

兩個類相等:類本身相等,并且還要使用同一個類加載器進行加載。這是因為每一個類加載器都擁有一個獨立的類名稱空間。

這里的相等,包括類的

  • Class 對象的 equals() 方法
  • isAssignableFrom() 方法
  • isInstance() 方法的返回結果為 true
  • 也包括使用 instanceof 關鍵字做對象所屬關系判定結果為 true。

2. 類加載器分類

從 Java 虛擬機的角度來講,只存在以下兩種不同的類加載器:

  • 啟動類加載器(Bootstrap ClassLoader),這個類加載器用C++實現(xiàn),是虛擬機自身的一部分
  • 所有其他類的加載器,這些類由 Java 實現(xiàn),獨立于虛擬機外部,并且全都繼承自抽象類 java.lang.ClassLoader。

從 Java 開發(fā)人員的角度看,類加載器可以劃分得更細致一些:

  • 啟動類加載器(Bootstrap ClassLoader):加載lib目錄下核心庫
  • 擴展類加載器(Extension ClassLoader):加載libext目錄下擴展包
  • 應用程序類加載器(Application ClassLoader): 加載用戶路徑(classpath)上指定的類庫。由于這個類加載器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般稱為系統(tǒng)類加載器
    如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。

3. 雙親委派模型

應用程序都是由三種類加載器相互配合進行加載的,如果有必要,還可以加入自己定義的類加載器。

下圖展示的類加載器之間的層次關系,稱為類加載器的雙親委派模型(Parents Delegation Model)。

該模型要求除了頂層的啟動類加載器外,其余的類加載器都應有自己的父類加載器

這里類加載器之間的父子關系一般通過組合(Composition)關系來實現(xiàn),而不是通過繼承的關系實現(xiàn)。

[圖片上傳失敗…(image-78b66d-1549615529620)]

(一)工作過程

一個類加載器首先將類加載請求傳送到父類加載器,只有當父類加載器無法完成類加載請求時才嘗試加載。

(二)好處

Java類伴隨其類加載器具備了帶有優(yōu)先級的層次關系,確保了在各種加載環(huán)境的加載順序。 保證了運行的安全性,防止不可信類扮演可信任的類。

例如 java.lang.Object 存放在 rt.jar 中,如果編寫另外一個 java.lang.Object 的類并放到 ClassPath 中,程序可以編譯通過。因為雙親委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 優(yōu)先級更高,因為 rt.jar 中的 Object 使用的是啟動類加載器,而 ClassPath 中的 Object 使用的是應用程序類加載器。正因為 rt.jar 中的 Object 優(yōu)先級更高,因為程序中所有的 Object 都是這個 Object。

(三)實現(xiàn)

以下是抽象類java.lang.ClassLoader的代碼片段,其中的 loadClass() 方法運行過程如下:

  • 先檢查類是否已經加載過,如果沒有則讓父類加載器去加載。當父類加載器加載失敗時拋出 ClassNotFoundException,此時嘗試自己去加載。
public abstract class ClassLoader { // The parent class loader for delegation private final ClassLoader parent; public Class> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } 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) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } } protected Class> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }}

4. 自定義類加載器實現(xiàn)

FileSystemClassLoader 是自定義類加載器,繼承自 java.lang.ClassLoader,用于加載文件系統(tǒng)上的類。它首先根據類的全名在文件系統(tǒng)上查找類的字節(jié)代碼文件(.class 文件),然后讀取該文件內容,最后通過 defineClass() 方法來把這些字節(jié)代碼轉換成 java.lang.Class 類的實例。

loadClass() 實現(xiàn)了雙親委派模型的邏輯,因此自定義類加載器一般不去重寫它,但是需要重寫 findClass() 方法。

public class FileSystemClassLoader extends ClassLoader { private String rootDir; public FileSystemClassLoader(String rootDir) { this.rootDir = rootDir; } protected Class> findClass(String name) throws ClassNotFoundException { byte[] classData = getClassData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getClassData(String className) { String path = classNameToPath(className); try { InputStream ins = new FileInputStream(path); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead; while ((bytesNumRead = ins.read(buffer)) != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } private String classNameToPath(String className) { return rootDir + File.separatorChar + className.replace('.', File.separatorChar) + ".class"; }}

雙親委派的理解

https://blog.csdn.net/inspiredbh/article/details/74889654

雙親委派模型的實現(xiàn)過程:

實現(xiàn)雙親委派模型的代碼都集中在java.lang.ClassLoader的loadClass()方法中:

1.當Application ClassLoader 收到一個類加載請求時,他首先不會自己去嘗試加載這個類,而是將這個請求委派給父類加載器Extension ClassLoader去完成。

2.當Extension ClassLoader收到一個類加載請求時,他首先也不會自己去嘗試加載這個類,而是將請求委派給父類加載器Bootstrap ClassLoader去完成。

3.如果Bootstrap ClassLoader加載失敗(在lib中未找到所需類),就會讓Extension ClassLoader嘗試加載。

4.如果Extension ClassLoader也加載失敗,就會使用Application ClassLoader加載。

5.如果Application ClassLoader也加載失敗,就會使用自定義加載器去嘗試加載

6.如果均加載失敗,就會拋出ClassNotFoundException異常。

雙親委派模型的優(yōu)點:

保證了運行的安全性,防止不可信類扮演可信任的類。

Class.forName()的理解

https://blog.csdn.net/fengyuzhengfan/article/details/38086743

Class.forName(className)可以簡單的理解為:獲得字符串參數(shù)中指定的類,并進行初始化操作。

Class.forName的一個很常見的用法是在加載數(shù)據庫驅動的時候。

首先你要明白在java里面任何class都要裝載在虛擬機上才能運行。

  • forName這句話就是裝載類用的(new是根據加載到內存中的類創(chuàng)建一個實例,要分清楚)。
  • 至于什么時候用,可以考慮一下這個問題,給你一個字符串變量,它代表一個類的包名和類名,你怎么實例化它?
  • A a = (A)Class.forName("pacage.A").newInstance();這和 A a =new A();是一樣的效果。
  • jvm在裝載類時會執(zhí)行類的靜態(tài)代碼段,要記住靜態(tài)代碼是和class綁定的,class裝載成功就表示執(zhí)行了你的靜態(tài)代碼了,而且以后不會再執(zhí)行這段靜態(tài)代碼了。
  • 動態(tài)加載和創(chuàng)建Class 對象,比如想根據用戶輸入的字符串來創(chuàng)建對象
  • String str = 用戶輸入的字符串 Class t = Class.forName(str); t.newInstance();

    newInstance()方法和new關鍵字最主要的區(qū)別

    • 一個是方法,一個是關鍵字
    • 它們的區(qū)別在于創(chuàng)建對象的方式不一樣,前者是使用類加載機制,后者是創(chuàng)建一個新類
    • 這主要考慮到軟件的可伸縮、可擴展和可重用等軟件設計思想。
    • newInstance: 弱類型。低效率。只能調用無參構造
    • new: 強類型。相對高效。能調用任何public構造

    JVM 參數(shù)

    GC 優(yōu)化配置

    配置 描述 -Xms 初始化堆內存大小 -Xmx 堆內存最大值 -Xmn 新生代大小 -XX:PermSize 初始化永久代大小 -XX:MaxPermSize 永久代最大容量

    GC 類型設置

    配置 描述 -XX:+UseSerialGC 串行垃圾回收器 -XX:+UseParallelGC 并行垃圾回收器 -XX:+UseConcMarkSweepGC 并發(fā)標記掃描垃圾回收器 -XX:ParallelCMSThreads= 并發(fā)標記掃描垃圾回收器 = 為使用的線程數(shù)量 -XX:+UseG1GC G1 垃圾回收器

    java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar java-application.jar

    關注我

    我是蠻三刀把刀,目前為后臺開發(fā)工程師。主要關注后臺開發(fā),網絡安全,Python爬蟲等技術。

    來微信和我聊聊:yangzd1102

    Github:https://github.com/qqxx6661

    原創(chuàng)博客主要內容

    • 筆試面試復習知識點手冊
    • Leetcode算法題解析(前150題)
    • 劍指offer算法題解析
    • Python爬蟲相關技術分析和實戰(zhàn)
    • 后臺開發(fā)相關技術分析和實戰(zhàn)

    同步更新以下博客

    1. Csdn

    http://blog.csdn.net/qqxx6661

    擁有專欄:Leetcode題解(Java/Python)、Python爬蟲開發(fā)

    2. 知乎

    https://www.zhihu.com/people/yang-zhen-dong-1/

    擁有專欄:碼農面試助攻手冊

    3. 掘金

    https://juejin.im/user/5b48015ce51d45191462ba55

    4. 簡書

    https://www.jianshu.com/u/b5f225ca2376

    個人公眾號:后端技術漫談

    如果文章對你有幫助,不妨收藏起來并轉發(fā)給您的朋友們~

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的虚拟机的分类_「面试必备」Java虚拟机知识点复习手册(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。