今日開篇
什麼是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.java
Copy
輸入完成後,對應的目錄下就會出現類的.class文件
-
在控制檯輸入 javap -v 類的絕對路徑
javap -v F:\Thread_study\src\com\nyima\JVM\day01\Main.class
Copy -
然後能在控制檯看到反編譯以後類的信息了
- 類的基本信息
- 常量池
虛擬機中執行編譯的方法(框內的是真正編譯執行的內容,#號的內容需要在常量池中查找)
運行時常量池
- 常量池
- 就是一張表(如上圖中的constant pool),虛擬機指令根據這張常量表找到要執行的類名、方法名、參數類型、字面量信息
- 運行時常量池
- 常量池是.class文件中的,當該*類被加載以後,它的常量池信息就會放入運行時常量池,並把裏面的符號地址變爲真實地址**