JVM(一)內存結構

今日開篇

什麼是JVM

定義

Java Virtual Machine,JAVA程序的運行環境(JAVA二進制字節碼的運行環境)

好處

  • 一次編寫,到處運行
  • 自動內存管理,垃圾回收機制
  • 數組下標越界檢查
  • 多態

學習JVM的作用

jvm的講解以hotspot爲準

整體架構

JVM內存結構

1、程序計數器

二進制字節碼、jvm指令——需要經過解釋器成爲機器碼,再給cpu執行

 

作用:是記住下一條jvm指令的執行地址

上圖的數字就是地址,放入程序計數器中,然後解釋器來讀

寄存器來當作程序計數器

特點

線程私有

  • CPU會爲每個線程分配時間片,噹噹前線程的時間片使用完以後,CPU就會去執行另一個線程中的代碼
  • 程序計數器是每個線程私有的,當另一個線程的時間片用完,又返回來執行當前線程的代碼時,通過程序計數器可以知道應該執行哪一句指令

不會存在內存溢出

 

2、虛擬機棧

  • 每個線程運行需要的內存空間,稱謂虛擬機棧

  • 每個棧由多個棧幀組成,對應着每次調用方法時所佔用的內存

  • 每個線程只能有一個活動棧幀,對應着當前正在執行的方法

棧幀就是每個方法運行需要的內存

演示

public class Main {
	public static void main(String[] args) {
		method1();
	}

	private static void method1() {
		method2(1, 2);
	}

	private static int method2(int a, int b) {
		int c = a + b;
		return c;
	}
}

不斷壓棧和出棧

問題辨析

  • 垃圾回收是否涉及棧內存?
    • 不需要。因爲虛擬機棧中是由一個個棧幀組成的,在方法執行完畢後,對應的棧幀就會被彈出棧。所以無需通過垃圾回收機制去回收內存。
  • 棧內存的分配越大越好嗎?
    • 不是。因爲物理內存是一定的,棧內存越大,可以支持更多的遞歸調用,但是可執行的線程數就會越少。
  • 方法內的局部變量是否是線程安全的?
    • 如果方法內局部變量沒有逃離方法的作用範圍,則是線程安全
    • 如果如果局部變量引用了對象,並逃離了方法的作用範圍,則需要考慮線程安全問題

棧是線程私有的,不會有線程不安全問題

線程安全

線程不安全

多個線程公用對象sb,返回值sb是對象可以被主線程修改也是不安全,基本類型不會出現返回值問題

是否逃離了方法的作用範圍

棧內存溢出

Java.lang.stackOverflowError 棧內存溢出

遞歸調用,需要正確的遞歸邊界

發生原因

  • 虛擬機棧中,棧幀過多(無限遞歸)
  • 每個棧幀所佔用過大

 

線程運行診斷

  • Linux環境下運行某些程序的時候,可能導致CPU的佔用過高,這時需要定位佔用CPU過高的線程
    • top命令,查看是哪個進程佔用CPU過高
    • ps H -eo pid, tid(線程id), %cpu | grep 剛纔通過top查到的進程號 通過ps命令進一步查看是哪個線程佔用CPU過高
    • jstack 進程id 通過查看進程中的線程的nid,剛纔通過ps命令看到的tid來對比定位,注意jstack查找出的線程id是16進制的需要轉換

死鎖的例子

 

3、本地方法棧

一些帶有native關鍵字的方法就是需要JAVA去調用本地的C或者C++方法,因爲JAVA有時候沒法直接和操作系統底層交互,所以需要用到本地方法。

java調用c語言編寫的方法---本地方法篇之java中的native關鍵字

c寫的代碼,用java調用

 

4、堆

heap

定義

通過new關鍵字創建的對象都會被放在堆內存

特點

  • 所有線程共享,堆內存中的對象都需要考慮線程安全問題
  • 有垃圾回收機制

堆內存溢出

java.lang.OutofMemoryError :java heap space. 堆內存溢出

堆內存診斷

 jconsole

案例:垃圾回收後,內存佔用依然很高

jvirsalvm

某個容器一直使用,沒有回收

5、方法區

p46頁

方法區與Java堆一樣,是各個線程共享的內存區域,它用於存儲已被虛擬機加載的類型信息、常量、靜態變量

 

內存溢出

  • 1.8以前會導致永久代內存溢出
  • 1.8以後會導致元空間內存溢出

元空間內存溢出

 永久代內存溢出

運行時常量池

二進制字節碼的組成:類的基本信息、常量池、類的方法定義(包含了虛擬機指令)

通過反編譯來查看類的信息

  • 獲得對應類的.class文件

    • 在JDK對應的bin目錄下運行cmd,也可以在IDEA控制檯輸入

 

  • 輸入 javac 對應類的絕對路徑
F:\JAVA\JDK8.0\bin>javac F:\Thread_study\src\com\nyima\JVM\day01\Main.javaCopy

輸入完成後,對應的目錄下就會出現類的.class文件

  • 在控制檯輸入 javap -v 類的絕對路徑

    javap -v F:\Thread_study\src\com\nyima\JVM\day01\Main.classCopy
  • 然後能在控制檯看到反編譯以後類的信息了

  • 類的基本信息

  •  常量池

虛擬機中執行編譯的方法(框內的是真正編譯執行的內容,#號的內容需要在常量池中查找

 

運行時常量池

  • 常量池
    • 就是一張表(如上圖中的constant pool),虛擬機指令根據這張常量表找到要執行的類名、方法名、參數類型、字面量信息
  • 運行時常量池
    • 常量池是.class文件中的,當該*類被加載以後,它的常量池信息就會放入運行時常量池,並把裏面的符號地址變爲真實地址**

 

 

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