ClassLoader類加載機制&&JVM內存管理


一、ClassLoader類加載機制

在java中類加載是遵循委派雙親加載的:通過調用loadClass方法逐級往上傳遞委派加載請求,當找不到父ClassLoader時調用其findClass方法嘗試進行查找和加載,如果當前ClassLo找不所需的Class,則由其孩子嘗試進行查找和加載,如果當前ClassLoader找了所需的Class則將該Class按請求路徑逐級返回孩子。其關系圖如下所示:

ClassLoader.loadClass(...) 是ClassLoader的入口點。當一個類沒有指明用什么加載器加載的時候,JVM默認采用AppClassLoader加載器加載沒有加載過的class,調用的方法的入口就是loadClass(...)。如果一個class被自定義的ClassLoader加載,那么JVM也會調用這個自定義的ClassLoader.loadClass(...)方法來加載class內部引用的一些別的class文件。重載這個方法,能實現自定義加載class的方式,拋棄雙親委托機制如果要實現自己的類加載器,只需繼承ClassLoader,重寫findClass方法。

二、內存管理

1.JVM內存模型

(1)方法區:存儲類的結構信息,類、類加載器元數據----永久代(堆的一部分),運行時常量池(每個class運行時的常量表)存儲已被JVM加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據 —XX:PermSize、—XX:MaxPermSize

(2)堆:存放Java對象

(3)棧:執行引擎:執行一個個方法的串行流程,是線程。每當創建一個線程時為其創建一個java棧和pc計數器,每調用一個方法則在棧中創建一個棧幀(保留方法的元信息:局部變量,返回地址等)

(4)本地方法棧:運行native方法的存儲空間

(5)靜態分配:編譯時已確定(局部變量(含原始數據類型)、對象引用(指向對象在堆中的地址)),棧幀分配,方法結束則消失 

(6)動態分配:程序執行時才確定,創建java對象時在堆分配 可共享 方法結束不一定消失, 內存回收已對象不再引用(直接或間接)為前期

Java文件從源文件到在JVM執行的流程如下:

JAVA棧結構圖:

2.內存回收依據

判斷對象可回收的依據是對象不再被引用,主要有兩種方法:

(1)計數法

給對象添加一個引用計數器,每當該對象被引用,它的計數器值就+1;當引用失效時,計數器就-1;在任何情況下,當計數器值為0時,就表示該對象不再被使用

(2)可達性分析

只要改對象不再被其他活動對象引用就可回收,活動對象是指從gc根對象有路徑達到該對象。gc根對象即對象的引用

3.回收算法

(1)標記清除算法

首先標記出所有需要回收的對象,而后在標記完成后統一回收所有被標記的對象。存在.效率問題,標記和清除兩個過程的效率都不高; 空間碎片問題,標記清除后會產生大量不連續的內存碎片,空間碎片太多可能會導致以后在程序運行中需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發另一個垃圾回收動作。

(2)復制算法

將內存划分為相等的兩塊,每次只使用其中一塊。當這一塊內存用完時,就將還存活的對象復制到另一塊上面,然后將已經使用過的內存空間一次清理掉。 

缺點:將內存縮小為了原來的一半,對內存空間耗費較大。在對象存活率較高時,需要進行多次復制操作,效率會變低。

(3)標記整理算法

不直接對可回收對象進行清理,而是讓所有存活對象都向另一端移動,然后直接清理掉端邊界以外的內存。

(4)分代收集算法

把對象按照壽命長短進行分組,分為新生代和老年代,然后根據各個年代的特點采用最適當的收集算法,在新生代采用復制算法,在老年代采用“標記-清除”或者“標記-整理”算法。

 

JVM將整個堆划分為Young區、Old區和Perm區,分別存放不同年齡的對象。

-Xms:堆起始大小

-Xmn:堆的最大大小,通常-Xms,-Xmn:設為一樣大,避免后期的堆收縮

-XX:SurvivorRatio:新生代中Eden區域與Survivor區域的容量比值,默認為8:1

-XX:PretenureSizeThreshold:新生代直接晉升到老年代的對象大小,大於這個參數的對象將直接在老年代分配

-XX:MaxTenuringThreshold:晉升到老年代的年齡。一個對象堅持過一次Minor GC后,年齡就增加1,當超過閥值就進入老年代

Young區分為Eden區和兩個相同大小的Survivor區,其中所有新創建的對象都分配在Eden區域中,當Eden區域滿后會觸發minor GC 將Eden區仍然存活的對象復制到其中一個Survivor區域中,另外一個Survivor區中的存活對象也復制到這個Survivor區域中,並始終保持一個Survivor區時空的。

Old區存放Young區Survivor滿后觸發minor GC后仍然存活的對象,當Eden區滿后會將存活的對象放入Survivor區域,如果Survivor區存不下這些對象,GC收集器就會將這些對象直接存放到Old區中,如果Survivor區中的對象足夠老,也直接存放到Old區中。如果Old區滿了,將會觸發Full GC回收整個堆內存。 

Perm區主要存放類的Class對象和常量,如果類不停地動態加載,也會導致Perm區滿。Perm區地垃圾回收也是有Full GC觸發地。

 

新創建的對象被分配在新生代,如果對象經過幾次回收后仍然存活,那么就把這個對象划分到老年代。老年代的收集頻度不象年輕代那么頻繁,這樣就減少了每次垃圾回收所需要掃描的對象,從而提高了垃圾回收效率。

 

Serial收集器是一個單線程收集器,它進行垃圾收集時,必須暫停其他所有的工作線程(Stop the world),直到它垃圾收集結束  client模式。 

ParNew收集器其實就是Serial收集器的多線程版本,在運行在Server模式下的虛擬機中,ParNew收集器是首選的新生代收集器。 

CMS收集器是一款並發收集器(用戶線程與垃圾收集線程同時執行),是一種以獲取最短回收停頓時間為目標的收集器,它是基於標記-清除算法實現的初始標記、重新標記仍然需要"Stop the World",但是它們的速度都很快。初始標記只是標記一下GC Roots能直接關聯到的對象,重新標記是為了修正並發標記期間因為用戶線程繼續運作而導致標記產生變動的那一部分對象的標記記錄。(初始標記→並發標記→重新標記→並發清除)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM