1. 程式人生 > 其它 >JVM(1):JVM的介紹

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 進位制的,需要轉換。