亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JVM內(nèi)存結(jié)構(gòu):程序計數(shù)器、虛擬機棧、本地方法棧

 更新時間:2021年06月11日 16:47:57   作者:興趣使然的草帽路飛  
JVM 基本上是每家招聘公司都會問到的問題,它們會這么無聊問這些不切實際的問題嗎?很顯然不是。由 JVM 引發(fā)的故障問題,無論在我們開發(fā)過程中還是生產(chǎn)環(huán)境下都是非常常見的

一、JVM 入門介紹

JVM 定義

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

JVM 優(yōu)勢

  • 一次編寫,到處運行
  • 自動內(nèi)存管理,垃圾回收機制
  • 數(shù)組下標越界檢查 常見的JVM

在這里插入圖片描述

:我們筆記所使用的的是HotSpot 版本

JVM JRE JDK的比較

JVM JRE JDK的區(qū)別:

img

學(xué)習(xí)步驟

學(xué)習(xí)順序如下圖:(由簡到難)

在這里插入圖片描述

二、內(nèi)存結(jié)構(gòu)

整體架構(gòu)

在這里插入圖片描述

1、程序計數(shù)器(寄存器)

Program Counter Register

在這里插入圖片描述

1.1 作用

程序計數(shù)器用于保存JVM中下一條所要執(zhí)行的指令的地址

0:getstatic #20 					 // PrintStream out = System.out;
1:astore_1 							// --
2:aload_1 							// out.println(1);
3:iconst_1 							// --
4:invokevirtual #26 				 // --
5:aload_1 						    // out.println(2);
6:iconst_2 							// --
7:invokevirtual #26 				 // --
8:aload_1 						    // out.println(3);
9:iconst_3 						    // --
10:invokevirtual #26 				 // --
11:aload_1 							// out.println(4);
12:iconst_4 						// --
13:invokevirtual #26 				 // --
14:aload_1 						    // out.println(5);
15:iconst_5 						// --
16:invokevirtual #26 				 // --
return

在這里插入圖片描述

Java指令執(zhí)行流程:

  • 每一條二進制字節(jié)碼(JVM指令) 通過 解釋器 轉(zhuǎn)換成 機器碼 然后 就可以被 CPU 執(zhí)行了!
  • 當(dāng) 解釋器 將一條jvm 指令轉(zhuǎn)換成 機器碼后 其會 向程序計數(shù)器 遞交 下一條 jvm 指令的執(zhí)行地址!
  • 程序計數(shù)器在硬件層面 其實是通過 寄存器 實現(xiàn)的!
  • 所以程序計數(shù)器的作用就是:用于保存JVM中下一條所要執(zhí)行的指令的地址!

1.2 特點

  • 線程私有
    • CPU會為每個線程分配時間片,當(dāng)當(dāng) 前線程的時間片使用完以后,CPU就會去執(zhí)行另一個線程中的代碼
    • 程序計數(shù)器是每個線程私有的,當(dāng)另一個線程的時間片用完,又返回來執(zhí)行當(dāng)前線程的代碼時,通過程序計數(shù)器可以知道應(yīng)該執(zhí)行哪一句指令
  • 不會存在內(nèi)存溢出

在這里插入圖片描述

2、虛擬機棧

Java Virtual Machine Stacks

在這里插入圖片描述

2.1 定義

  • 每個線程運行需要的內(nèi)存空間,這一空間被稱為虛擬機棧(Frames)
  • 每個棧由多個棧幀(Frame) 組成,對應(yīng)著每個方法運行時所占用的內(nèi)存
  • 每個線程只能有一個活動棧幀,對應(yīng)著當(dāng)前正在執(zhí)行的方法,當(dāng)方法執(zhí)行時壓入棧,方法執(zhí)行完畢后 彈出棧

2.2 演示

在這里插入圖片描述

代碼

/**
 * @Auther: csp1999
 * @Date: 2020/11/10/11:36
 * @Description: 演示棧幀
 */
public class Demo01 {
    public static void main(String[] args) {
        methodA();
    }
    private static void methodA() {
        methodB(1, 2);
    }
    private static int methodB(int a, int b) {
        int c = a + b;
        return c;
    }
}

我們打斷點來Debug 一下看一下方法執(zhí)行的流程:

在這里插入圖片描述

接這往下走,使方法B執(zhí)行完畢:

在這里插入圖片描述

然后方法A執(zhí)行完畢,其對應(yīng)的棧幀出棧,main方法對應(yīng)的棧幀為活動棧幀;最后main執(zhí)行完畢 棧幀出棧,虛擬機棧為空,代碼運行結(jié)束!

2.3 面試問題辨析

  • 1.垃圾回收是否涉及棧內(nèi)存?
    • 不需要。因為虛擬機棧中是由一個個棧幀組成的,在方法執(zhí)行完畢后,對應(yīng)的棧幀就會被彈出棧。所以無需通過垃圾回收機制去回收內(nèi)存。
  • 2.棧內(nèi)存的分配越大越好嗎?

在這里插入圖片描述

  • 不是。因為物理內(nèi)存是一定的,棧內(nèi)存越大,可以支持更多的遞歸調(diào)用,但是可執(zhí)行的線程數(shù)就會越少。
  • 舉例:如果物理內(nèi)存是500M(假設(shè)),如果一個線程所能分配的棧內(nèi)存為2M的話,那么可以有250個線程。而如果一個線程分配棧內(nèi)存占5M的話,那么最多只能有100 個線程同時執(zhí)行!

3.方法內(nèi)的局部變量是否是線程安全的?

在這里插入圖片描述


在這里插入圖片描述

從圖中得出:局部變量如果是靜態(tài)的可以被多個線程共享,那么就存在線程安全問題。如果是非靜態(tài)的只存在于某個方法作用范圍內(nèi),被線程私有,那么就是線程安全的!

看一個案例:

/**
 * 局部變量的線程安全問題
 */
public class Demo02 {
    public static void main(String[] args) {// main 函數(shù)主線程
        StringBuilder sb = new StringBuilder();
        sb.append(4);
        sb.append(5);
        sb.append(6);
        new Thread(() -> {// Thread新創(chuàng)建的線程
            m2(sb);
        }).start();
    }
    public static void m1() {
        // sb 作為方法m1()內(nèi)部的局部變量,是線程私有的 ---> 線程安全
        StringBuilder sb = new StringBuilder();
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }
    public static void m2(StringBuilder sb) {
        // sb 作為方法m2()外部的傳遞來的參數(shù),sb 不在方法m2()的作用范圍內(nèi)
        // 不是線程私有的 ---> 非線程安全
        sb.append(1);
        sb.append(2);
        sb.append(3);
        System.out.println(sb.toString());
    }
    public static StringBuilder m3() {
        // sb 作為方法m3()內(nèi)部的局部變量,是線程私有的
        StringBuilder sb = new StringBuilder();// sb 為引用類型的變量
        sb.append(1);
        sb.append(2);
        sb.append(3);
        return sb;// 然而方法m3()將sb返回,sb逃離了方法m3()的作用范圍,且sb是引用類型的變量
        // 其他線程也可以拿到該變量的 ---> 非線程安全
        // 如果sb是非引用類型,即基本類型(int/char/float...)變量的話,逃離m3()作用范圍后,則不會存在線程安全
    }
}

該面試題答案:

如果方法內(nèi)局部變量沒有逃離方法的作用范圍,則是線程安全

如果局部變量引用了對象,并逃離了方法的作用范圍,則需要考慮線程安全問題

2.4 內(nèi)存溢出

Java.lang.stackOverflowError 棧內(nèi)存溢出

發(fā)生原因

在這里插入圖片描述

  • 1.虛擬機棧中,棧幀過多(無限遞歸),這種情況比較常見
  • 2.每個棧幀所占用內(nèi)存過大(某個/某幾個棧幀內(nèi)存直接超過虛擬機棧最大內(nèi)存),這種情況比較少見!

舉2個案例:

案例1:

/**
 * 演示棧內(nèi)存溢出 java.lang.StackOverflowError
 * -Xss256k 可以通過棧內(nèi)存參數(shù) 設(shè)置棧內(nèi)存大小
 */
public class Demo03 {
    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++;// 統(tǒng)計棧幀個數(shù)
        method1();// 方法無限遞歸,不斷產(chǎn)生棧幀 到虛擬機棧
    }
}
最后輸出結(jié)果:
java.lang.StackOverflowError
	at com.haust.jvm_study.demo.Demo03.method1(Demo03.java:21)
     ...
     ...
39317// 棧幀個數(shù),不同的虛擬機大小能存放的棧幀數(shù)量不一樣

我們可以通過修改參數(shù)來指定虛擬機棧內(nèi)存大小

在這里插入圖片描述

當(dāng)我們將虛擬機棧內(nèi)存縮小到指定的256k的時候再運行Demo03后,會得到其棧內(nèi)最大棧幀數(shù)為:3816 遠小于原來的39317!

案例2:

/**
 * 兩個類之間的循環(huán)引用問題,導(dǎo)致的棧溢出
 * 
 * 解決方案:打斷循環(huán),即在員工emp 中忽略其dept屬性,放置遞歸互相調(diào)用
 */
public class Demo04 {
    public static void main(String[] args) throws JsonProcessingException {
        Dept d = new Dept();
        d.setName("Market");
        Emp e1 = new Emp();
        e1.setName("csp");
        e1.setDept(d);
        Emp e2 = new Emp();
        e2.setName("hzw");
        e2.setDept(d);
        d.setEmps(Arrays.asList(e1, e2));
        // 輸出結(jié)果:{"name":"Market","emps":[{"name":"csp"},{"name":"hzw"}]}
        ObjectMapper mapper = new ObjectMapper();// 要導(dǎo)入jackson包
        System.out.println(mapper.writeValueAsString(d));
    }
}
/**
 * 員工
 */
class Emp {
    private String name;
    @JsonIgnore// 忽略該屬性:為啥呢?我們來分析一下!
    /**
     * 如果我們不忽略掉員工對象中的部門屬性
     * System.out.println(mapper.writeValueAsString(d));
     * 會出現(xiàn)下面的結(jié)果:
     * {
     *  "name":"Market","emps":
     *  [c
     *      {"name":"csp",dept:{name:'xxx',emps:'...'}},
     *      ...
     *  ]
     * }
     * 也就是說,輸出結(jié)果中,部門對象dept的json串中包含員工對象emp,
     * 而員工對象emp 中又包含dept,這樣互相包含就無線遞歸下去,json串越來越長...
     * 直到棧溢出!
     */
    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 線程運行診斷

案例1:CPU占用過高

  • Linux環(huán)境下運行某些程序的時候,可能導(dǎo)致CPU的占用過高,這時需要定位占用CPU過高的線程
  • top命令,查看是哪個進程占用CPU過高

在這里插入圖片描述


在這里插入圖片描述

  • ps H -eo pid, tid(線程id), %cpu | grep 剛才通過top查到的進程號 通過ps命令進一步查看具體是哪個線程占用CPU過高!

在這里插入圖片描述

  • jstack 進程id 通過查看進程中的線程的nid,剛才通過ps命令看到的tid來對比定位,注意jstack查找出的線程id是16進制的,需要轉(zhuǎn)換
    • 可以通過線程id,找到有問題的線程,進一步定位到問題代碼的源碼行數(shù)!

在這里插入圖片描述


在這里插入圖片描述

我們可以看到上圖中的thread1 線程一直在運行(runnable)中,說明就是它占用了較高的CPU內(nèi)存;

在這里插入圖片描述


3、本地方法棧

在這里插入圖片描述

在這里插入圖片描述

一些帶有native 關(guān)鍵字的方法就是需要JAVA去調(diào)用本地的C或者C++方法,因為JAVA有時候沒法直接和操作系統(tǒng)底層交互,所以需要用到本地方法!

如圖:


在這里插入圖片描述


在這里插入圖片描述

  • 本地接口的作用是融合不同的編程語言為Java所用,它的初衷是融合C/C++程序,Java誕生的時候是C/C++橫行的時候,要想立足,必須由調(diào)用C/C++程序,于是就在內(nèi)存中專門開辟了一塊區(qū)域處理標記為native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時加載native libraies
  • 目前該方法的使用的越來越少了,除非是與硬件有關(guān)的應(yīng)用,比如通過Java程序驅(qū)動打印機或者Java系統(tǒng)管理生產(chǎn)設(shè)備,在企業(yè)級應(yīng)用中已經(jīng)比較少見。因為現(xiàn)在的異構(gòu)領(lǐng)域間的通信很發(fā)達,比如可以使用Socket通信,也可以使用Web Service等等,不多做介紹
  • 本地方法棧(Native Method Stack):(它的具體做法是Native Method Stack中登記native方法,在Execution Engine 執(zhí)行時加載本地方法庫)
  • native方法的舉例: Object類中的clone wait notify hashCode 等 Unsafe類都是native方法

4、總結(jié)

這篇文章的內(nèi)容就到這了,希望大家多多關(guān)注腳本之家的其他內(nèi)容!

相關(guān)文章

  • Flink流處理引擎零基礎(chǔ)速通之?dāng)?shù)據(jù)的抽取篇

    Flink流處理引擎零基礎(chǔ)速通之?dāng)?shù)據(jù)的抽取篇

    今天不分享基礎(chǔ)概念知識了,來分享一個馬上工作需要的場景,要做數(shù)據(jù)的抽取,不用kettle,想用flink。實際就是flink的sql、table層級的api
    2022-05-05
  • Servlet從入門到精通(超級詳細!)

    Servlet從入門到精通(超級詳細!)

    在JavaWeb項目中,處理請求和發(fā)送響應(yīng)的過程是由一種叫做Servlet 的程序來完成的,并且 Servlet 是為了解決實現(xiàn)動態(tài)頁面而衍生的東西,下面這篇文章主要給大家介紹了關(guān)于Servlet從入門到精通的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • Java結(jié)合redistemplate使用分布式鎖案例講解

    Java結(jié)合redistemplate使用分布式鎖案例講解

    在Java中使用RedisTemplate結(jié)合Redis來實現(xiàn)分布式鎖是一種常見的做法,特別適用于微服務(wù)架構(gòu)或多實例部署的應(yīng)用程序中,以確保數(shù)據(jù)的一致性和避免競態(tài)條件,下面給大家分享使用Spring Boot和RedisTemplate實現(xiàn)分布式鎖的案例,感興趣的朋友一起看看吧
    2024-08-08
  • java ThreadPool線程池的使用,線程池工具類用法說明

    java ThreadPool線程池的使用,線程池工具類用法說明

    這篇文章主要介紹了java ThreadPool線程池的使用,線程池工具類用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • idea普通javaweb項目如何部署到tomcat(讀取web.xml文件)

    idea普通javaweb項目如何部署到tomcat(讀取web.xml文件)

    這篇文章主要介紹了idea普通javaweb項目如何部署到tomcat(讀取web.xml文件),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • springboot實現(xiàn)maven多模塊和打包部署

    springboot實現(xiàn)maven多模塊和打包部署

    本文主要介紹了springboot實現(xiàn)maven多模塊和打包部署,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • java多線程實現(xiàn)有序輸出ABC

    java多線程實現(xiàn)有序輸出ABC

    這篇文章主要為大家詳細介紹了java多線程實現(xiàn)有序輸出ABC,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • JVM內(nèi)存結(jié)構(gòu)相關(guān)知識解析

    JVM內(nèi)存結(jié)構(gòu)相關(guān)知識解析

    這篇文章主要介紹了JVM內(nèi)存結(jié)構(gòu)相關(guān)知識解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • Java反射中java.beans包學(xué)習(xí)總結(jié)

    Java反射中java.beans包學(xué)習(xí)總結(jié)

    本篇文章通過學(xué)習(xí)Java反射中java.beans包,吧知識點做了總結(jié),并把相關(guān)內(nèi)容做了關(guān)聯(lián),對此有需要的朋友可以學(xué)習(xí)參考下。
    2018-02-02
  • java 利用反射獲取內(nèi)部類靜態(tài)成員變量的值操作

    java 利用反射獲取內(nèi)部類靜態(tài)成員變量的值操作

    這篇文章主要介紹了java 利用反射獲取內(nèi)部類靜態(tài)成員變量的值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論