柚子快報邀請碼778899分享:JVM簡單學習
柚子快報邀請碼778899分享:JVM簡單學習
jvm與字節(jié)碼
jvm只需關注字節(jié)碼文件 jvm由哪些部分構(gòu)成 1.類加載子系統(tǒng),將磁盤中的字節(jié)碼文件加載到方法區(qū)的內(nèi)存空間中 類加載器分兩種:引導類加載器是jvm底層中用C和C++語言寫的
各個默認的類加載器的不同區(qū)別在于 各自默認負責要加載的類的目錄不一樣 比如 BootStrapClassLoader:負責jre/lib 目錄下 ExtClassLoader:負責jre/lib/ext 目錄下 AppClassLoader:負責 classpath目錄下
什么是雙親委派 和 雙親委派的兩大作用
避免類的重復加載防止核心API被篡改
什么是雙親委派機制
雙親委派機制(Parent Delegation Mechanism)是Java中的一種類加載機制。在Java中,類加載器負責加載類的字節(jié)碼并創(chuàng)建對應的Class對象。雙親委派機制是指當一個類加載器收到類加載請求時,它會先將該請求委派給它的父類加載器去嘗試加載。只有當父類加載器無法加載該類時,子類加載器才會嘗試加載。
這種機制的設計目的是為了保證類的加載是有序的,避免重復加載同一個類。Java中的類加載器形成了一個層次結(jié)構(gòu),根加載器(Bootstrap ClassLoader)位于最頂層,它負責加載Java核心類庫。其他加載器如擴展類加載器(Extension ClassLoader)和應用程序類加載器(Application ClassLoader)都有各自的加載范圍和職責。通過雙親委派機制,可以確保類在被加載時,先從上層的加載器開始查找,逐級向下,直到找到所需的類或者無法找到為止。
這種機制的好處是可以避免類的重復加載,提高了類加載的效率和安全性。同時,它也為Java提供了一種擴展機制,允許開發(fā)人員自定義類加載器,實現(xiàn)特定的加載策略。
雙親委派機制的原理
雙親委派機制是Java中的一種類加載機制。其原理如下:
當Java程序需要加載一個類時,首先會委托給當前類加載器的父類加載器進行加載。 父類加載器會按照相同的方式嘗試加載該類。如果父類加載器能夠成功加載該類,則加載過程結(jié)束。 如果父類加載器無法加載該類,則會將加載請求再次委托給它的父類加載器,直到達到頂層的引導類加載器。 引導類加載器是Java虛擬機內(nèi)置的類加載器,它負責加載核心類庫,如java.lang包下的類。 如果引導類加載器也無法加載該類,則會回到初始的類加載器,嘗試使用自身的加載機制加載該類。 如果自身的加載機制仍然無法加載該類,則會拋出ClassNotFoundException異常。
通過這種雙親委派的機制,Java實現(xiàn)了類加載的層次結(jié)構(gòu)。它可以確保類的加載是有序的,避免了重復加載和類的沖突。同時,它也提供了一種安全機制,防止惡意代碼的加載和執(zhí)行。
主要實現(xiàn)在ClassLoader里的loadClass方法
雙親委派主要體現(xiàn)在 ClassLoader#loadClass() 方法中:
PS:每個 ClassLoader 都有如下三個基礎方法:
loadClass()
入口,定義了 加載/尋找 Class 的策略。比如雙親委派,或者其他形式調(diào)用 findClass() 讀入 class 文件,并生成 Class 對象 findClass()
根據(jù) class 名,在當前 ClassLoader 能處理路徑中查找,如果能找到就將 class 文件讀入調(diào)用 defineClass 生成 Class 對象 defineClass()
native 方法將字節(jié)數(shù)組生成 Class 對象
// resolve = false 不進行解析
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 查看該類是否被加載過
Class> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 獲取當前類父類的加載器
if (parent != null) {
// 使用父類的加載器加載?。?!
// 遞歸
c = parent.loadClass(name, false);
} else {
// 如果 parent 為 null,說明父類加載器為引導類加載器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
// 當前類的加載器父類加載器未加載此類 or 當前類的加載器未加載此類
if (c == null) {
long t1 = System.nanoTime();
// 調(diào)用當前 Classloader 的 findClass
// 注:可能當前 classloader 無法處理要加載的這個類的路徑,這時返回 null
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
// 根據(jù)入?yún)Q定是否解析
if (resolve) {
resolveClass(c);
}
return c;
}
}
如何自定義類加載器,下面是一個自定義 ClassLoader 示例,直接繼承 ClassLoader 類,然后重寫 findClass 方法就行了(采用雙親委派)
public class MyClassLoader extends ClassLoader{
// 指定的要加載 class 文件的目錄
private File classPathFile;
public MyClassLoader(String absolutePath) {
this.classPathFile = new File(absolutePath);
}
@Override
// 根據(jù)類名將指定類加載進 JVM
// 注:該 class 必修在指定的 absolutePath 下
protected Class> findClass(String name) throws ClassNotFoundException {
// 拼接全類名
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile != null){
// 根據(jù)絕對路徑,以及 class 文件名,拿到 class 文件
// 注意全類名的情況
File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
// 如果 class 文件存在
if(classFile.exists()){
// 將 class 文件讀入內(nèi)存,暫存到一個字節(jié)數(shù)組中
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
// 構(gòu)造類的 Class 對象?。?!
// 注:defineClass() 是一個 native 方法
return defineClass(className, out.toByteArray(), 0, out.size());
}catch (Exception e){
e.printStackTrace();
}
}
}
return null;
}
}
運行時數(shù)據(jù)區(qū)
程序計數(shù)器
Java方法棧
一個方法對應一個棧幀,方法內(nèi)有其他方法就會依次入棧,執(zhí)行完的方法對應的棧幀會出棧
OOM是內(nèi)存不夠, StackOverflowError 是方法調(diào)用層次太多
棧幀
局部變量表
slot 代表各個方法內(nèi)的局部變量
操作數(shù)棧
執(zhí)行步驟
10 壓入操作數(shù)棧將 10 放入局部變量表 2將20 壓入操作數(shù)棧將20 放入局部變量表2iload1 將 局部變量表1放入操作數(shù)棧 iload2 將 局部變量表2放入操作數(shù)棧iadd 操作數(shù)棧中相加,得到的值放入局部變量3
本地方法棧
堆
Java對象在各個區(qū)域的流轉(zhuǎn)情況
對象首先來到Eden區(qū),Eden滿了后進行一次GC,沒被清理的對象來到S0區(qū)計數(shù)加一,Eden迎接新對象,每次GC存活對象計數(shù)加一,計數(shù)達到閾值進入老年代,如果有大對象(文件上傳對象)S0和S1放不下直接進入老年代
各個GC的區(qū)別
老年代只有一個CMS專門回收
為什么要進行垃圾回收
垃圾標記階段,如何標記垃圾
引用計數(shù)法
可達性分析法
GCRoots包括哪些
虛擬機棧和本地方法棧中的方法參數(shù)和局部變量存儲的是對象的地址值
GC算法
標記清除算法
缺點
效率不高會產(chǎn)生內(nèi)存碎片 優(yōu)點
簡單
復制算法
利用空間換時間,適合垃圾對象比較多,非垃圾對象少,這樣復制成本就低效率就高。 新生代適合用復制算法,因為對象朝生夕死
標記整理算法
算法總結(jié)
一種GC算法的使用理念
算法按需使用,用到最適合他的地方
常見的垃圾收集器
CMS 如何工作
并發(fā)標記清除算法,特點低暫停,STW時間短,執(zhí)行過程長,工作線程暫停時間短影響小,所以用戶體驗好,但是吞吐量變低
階段一 初始標記:
STW暫停所有工作線程標記出GCRoots能直接可達對象(以GCRoots為起點的第一層引用對象,不再向下標記)一旦標記,就回復工作線程繼續(xù)執(zhí)行這個階段比較短,所以STW時間短 階段二 并發(fā)標記(最費時的階段,但是不STW不影響工作線程工作): 因為工作線程也在運行中,標記垃圾可能會有誤差,因為程序在運行就有可能產(chǎn)生垃圾對象,但是誤差不大 階段三和階段四 重新標記短暫STW后并發(fā)清理
CMS存在的問題
G1是如何工作的
G1垃圾回收器下,堆空間不再是其他回收器那樣的各個代之間層次分明,而是把堆空間分成2048個Region進行管理,各個代空間邏輯上連續(xù),但是物理上不再連續(xù)
G1的初始標記和并發(fā)標記與CMS相同
G1和CMS最大區(qū)別在最后一步,CMS是清除算法,G1是復制算法,復制到空閑的region
G1的篩選回收
可以指定GC的STW停頓時間,CMS的STW時間是不確定的,相較于CMS的清除算法,G1是把需要清理的Region中的非垃圾對象復制到空閑的Region區(qū)域
柚子快報邀請碼778899分享:JVM簡單學習
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權,聯(lián)系刪除。