Java文件的運行

class文件

一個Java文件通過編譯工具javac編譯成class字節碼文件,通過JVM進行加載運行。因爲JVM屏蔽底層操作系統的差異,所以一次編譯到處運行。

JVM內存結構

在這裏插入圖片描述
JVM分爲外部和內部,內部就是數據區,其他部分就是外部

  • 線程私有數據區隨着線程結束而結束,沒有安全問題;
  • 線程公有數據區存在安全問題,內存優化和溢出都需要關注這個地方。

類加載子系統

class文件
加載
驗證
準備
解析
初始化
  • 加載:通過類的完全限定名來找到類文件所在位置,根據字節碼創建java.lang.class對象
  • 驗證:確保加載的字節碼符合虛擬機要求,是一種自我保護機制,不讓危害虛擬機安全。包括四種驗證:字節碼驗證、文件格式驗證、元數據驗證、符號引用驗證。
  • 準備:爲類變量分配地址和初始化值,分配到方法區。初始值是數值類型默認的初始值。
  • 解析:把符號引用轉換爲直接引用。
  • 初始化:爲變量賦予正確值。

雙親委派方式

自底向上檢查是否加載成功,自頂向下嘗試加載。

收到類加載請求後,先加載父類,父類加載完才加載子類。如果父類以上還存在父類,那麼繼續加載上面的父類,直到最高的父類加載完才往下加載。

類加載的三種方式

以下示範代碼中,A是類,a1、a2、a3是A實例化的對象

A a1=new A();

靜態加載,通過new關鍵字實例化對象

Class clazz=Class.forName("A");
Object a2=a.newInstance();

動態加載,通過forName來加載類,調用類的newInstance方法實例化對象

Class clazz=classLoader.loadClaass("A");
Object a3=clazz.newInstance();

動態加載,繼承ClassLoader實現自定義類加載器

程序計數器

cpu進行切換的時候,指向當前需要獲取指令的位置,線程私有,記錄程序執行位置,不會發生內存溢出,只佔一小塊區域。

虛擬機棧

作用是執行方法。棧的順序,先進後出。先執行的方法在下面,按順序放入,執行完畢從上往下退出。執行方法叫做壓棧,結束方法叫做出棧(銷燬棧幀)。

棧幀是虛擬機棧的單位,一個方法一個棧幀,一個棧幀分爲四部分:

  • 局部變量表:存放方法參數或內部定義的變量列表,比如對象;
  • 操作數棧:在執行方法的時候使用;
  • 動態鏈接:和上面的解析一樣,將符號引用轉換爲直接引用
  • 返回地址:不論方法成功失敗,返回方法被調用的地址

棧溢出

當棧的深度大於虛擬機,報StackOverflowError,-Xss可設置大小

內存溢出

當棧需要擴展而無法申請空間,報OutOfMemoryError

本地方法棧

和虛擬機棧類似。虛擬機棧爲JVM執行字節碼服務,本地方法棧爲本地(native)方法服務。

  • Sun HotSpot將虛擬機棧和本地方法棧合併;
  • 有StackOverflowError和OutOfMemoryError;

方法區(元空間)

加載類的時候,將類的一些元數據信息保存在這裏,比如變量,方法。方法區是線程共享的。

  • jdk1.7合併方法區到堆裏
  • jdk1.8保留方法區,稱之爲元空間
  • HotSpot虛擬機中稱之爲永久代
  • 存在OutOfMemoryError,可以通過-XX:MaxPermSize設置大小

用於存放所有實例化對象和數組,是JVM分配內存最大的區域,在虛擬機啓動時創建,用垃圾回收器管理,分爲新生代(1/3)和老年代(2/3)。
新生代還可以分爲Eden、From Survivor、To Survivor空間,比例爲8:1:1,通過-Xmx,-Xms設置大小

新生代

新生代大部分對象不需要太長的存活時間,會頻繁觸發Minor GC進行回收

老年代

老年代存放的是存活時間較久或內存較大的對象,不會頻繁執行Full GC

Minor GC

新生代的垃圾回收機制,採用複製算法(掃描存活對象,複製到新內存區域)。
發生時,Eden區和一個Survivor區把存活的對象放到另一個Survivor區,然後清理自己。當達到一定年齡以後,把對象放入老年代。每發生一次Minor GC增加一歲,默認15歲。

Full GC

老年代的垃圾回收機制,採用標記清除(標記存活對象,清除未標記對象)。
發生的頻率比Minor GC低,但一次的時間會更長。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章