JVM(1):JVM的介紹
一、 什麼是JVM
定義
Java Virtual Machine,JAVA程式的執行環境(JAVA二進位制位元組碼的執行環境)
好處
- 一次編寫,到處執行
- 自動記憶體管理,垃圾回收機制
- 陣列下標越界檢查
比較
JVM JRE JDK的區別
學習 JVM 有什麼用?
面試必備
中高階程式設計師必備
想走的長遠,就需要懂原理,比如:自動裝箱、自動拆箱是怎麼實現的,反射是怎麼實現的,垃圾回收機制是怎麼回事等待,JVM 是必須掌握的。
常見的 JVM
我們主要學習的是 HotSpot 版本的虛擬機器。
JVM整體架構
二、記憶體結構
1、程式計數器
Program Counter Register 程式計數器(暫存器)
1.1 作用
- 直譯器會解釋指令為機器碼交給 cpu 執行,程式計數器會記錄下一條指令的地址行號,這樣下一次直譯器會從程式計數器拿到指令然後進行解釋執行。
- 多執行緒的環境下,如果兩個執行緒發生了上下文切換,那麼程式計數器會記錄執行緒下一行指令的地址行號,以便於接著往下執行
1.2 特點
- 執行緒私有
1、CPU會為每個執行緒分配時間片,噹噹前執行緒的時間片使用完以後,CPU就會去執行另一個執行緒中的程式碼。
2、程式計數器是每個執行緒所私有的
,當另一個執行緒的時間片用完,又返回來執行當前執行緒的程式碼時,通過程式計數器可以知道應該執行哪一句指令。 - 不會存在記憶體溢位
2、虛擬機器棧
2.2 定義
- 每個執行緒執行需要的記憶體空間,稱為虛擬機器棧
- 每個棧由多個棧幀組成,對應著每次呼叫方法時所佔用的記憶體
- 每個執行緒只能有一個活動棧幀,對應著當前正在執行的方法
程式碼演示
package com.ali.jvm; public class Test { 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; } }
在控制檯中可以看到,主類中的方法在進入虛擬機器棧的時候,符合棧的特點,每一個方法都是一個棧幀,最先進入的方法是main方法,所以main方法對應的棧幀壓在最下面,最後進的方法是method2,所以method2對應的棧幀在最上面
2.3 問題辨析
-
垃圾回收是否涉及棧記憶體?
不需要。因為虛擬機器棧中是由一個個棧幀組成的,在方法執行完畢後,對應的棧幀就會被彈出棧。所以無需通過垃圾回收機制去回收記憶體。
-
棧記憶體的分配越大越好嗎?
不是。因為實體記憶體是一定的,棧記憶體越大,可以支援更多的遞迴呼叫,但是可執行的執行緒數就會越少。
-
方法內的區域性變數是否是執行緒安全的?
如果方法內區域性變數沒有逃離方法的作用範圍,則是執行緒安全的
如果如果區域性變數引用了物件,並逃離了方法的作用範圍,則需要考慮執行緒安全問題
2.4 棧記憶體溢位
Java.lang.stackOverflowError 棧記憶體溢位
發生原因
- 虛擬機器棧中,
棧幀過多(無限遞迴)
- 每個
棧幀所佔用過大
程式碼演示1:
package cn.itcast.jvm.t1.stack;
/**
* 演示棧記憶體溢位 java.lang.StackOverflowError
* -Xss256k
*/
public class Demo1_2 {
private static int count;
public static void main(String[] args) {
try {
method1();
} catch (Throwable e) {
e.printStackTrace();
System.out.println(count);
}
}
private static void method1() {
count++;
method1();
}
}
圖上所示就是無限遞迴,當呼叫了一萬八千多次之後,出現棧記憶體溢位
程式碼演示二:
package cn.itcast.jvm.t1.stack;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Arrays;
import java.util.List;
/**
* json 資料轉換
*/
public class Demo1_19 {
public static void main(String[] args) throws JsonProcessingException {
Dept d = new Dept();
d.setName("Market");
Emp e1 = new Emp();
e1.setName("zhang");
e1.setDept(d);
Emp e2 = new Emp();
e2.setName("li");
e2.setDept(d);
d.setEmps(Arrays.asList(e1, e2));
// { name: 'Market', emps: [{ name:'zhang', dept:{ name:'', emps: [ {}]} },] }
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(d));
}
}
class Emp {
private String name;
// @JsonIgnore
private Dept dept;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
class Dept {
private String name;
private List<Emp> emps;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Emp> getEmps() {
return emps;
}
public void setEmps(List<Emp> emps) {
this.emps = emps;
}
}
2.5 執行緒執行診斷案例一
:CPU佔用過高解決方法
:Linux 環境下執行某些程式的時候,可能導致 CPU 的佔用過高,這時需要定位佔用 CPU 過高的執行緒
1、top 命令,檢視是哪個程序佔用 CPU 過高
2、ps H -eo pid, tid(執行緒id), %cpu | grep 剛才通過 top 查到的程序號 通過 ps 命令進一步檢視是哪個執行緒佔用 CPU 過高
3、jstack 程序 id 通過檢視程序中的執行緒的 nid ,剛才通過 ps 命令看到的 tid 來對比定位,注意 jstack 查找出的執行緒 id 是 16 進位制的,需要轉換。