欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

一、JVM 入門(mén)介紹

JVM 定義

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

JVM 優(yōu)勢(shì)

  • 一次編寫(xiě),到處運(yùn)行
  • 自動(dòng)內(nèi)存管理,垃圾回收機(jī)制
  • 數(shù)組下標(biāo)越界檢查 常見(jiàn)的JVM

在這里插入圖片描述

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

JVM JRE JDK的比較

JVM JRE JDK的區(qū)別:

img

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

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

在這里插入圖片描述

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

整體架構(gòu)

在這里插入圖片描述

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

Program Counter Register

在這里插入圖片描述

1.1 作用

程序計(jì)數(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í)行流程:

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

1.2 特點(diǎn)

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

在這里插入圖片描述

2、虛擬機(jī)棧

Java Virtual Machine Stacks

在這里插入圖片描述

2.1 定義

  • 每個(gè)線(xiàn)程運(yùn)行需要的內(nèi)存空間,這一空間被稱(chēng)為虛擬機(jī)棧(Frames)
  • 每個(gè)棧由多個(gè)棧幀(Frame) 組成,對(duì)應(yīng)著每個(gè)方法運(yùn)行時(shí)所占用的內(nèi)存
  • 每個(gè)線(xiàn)程只能有一個(gè)活動(dòng)棧幀,對(duì)應(yīng)著當(dāng)前正在執(zhí)行的方法,當(dāng)方法執(zhí)行時(shí)壓入棧,方法執(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;
    }
}

我們打斷點(diǎn)來(lái)Debug 一下看一下方法執(zhí)行的流程:

在這里插入圖片描述

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

在這里插入圖片描述

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

2.3 面試問(wèn)題辨析

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

在這里插入圖片描述

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

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

在這里插入圖片描述


在這里插入圖片描述

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

看一個(gè)案例:

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

該面試題答案:

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

如果局部變量引用了對(duì)象,并逃離了方法的作用范圍,則需要考慮線(xiàn)程安全問(wèn)題

2.4 內(nèi)存溢出

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

發(fā)生原因

在這里插入圖片描述

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

舉2個(gè)案例:

案例1:

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

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

在這里插入圖片描述

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

案例2:

/**
 * 兩個(gè)類(lèi)之間的循環(huán)引用問(wè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// 忽略該屬性:為啥呢?我們來(lái)分析一下!
    /**
     * 如果我們不忽略掉員工對(duì)象中的部門(mén)屬性
     * System.out.println(mapper.writeValueAsString(d));
     * 會(huì)出現(xiàn)下面的結(jié)果:
     * {
     *  "name":"Market","emps":
     *  [c
     *      {"name":"csp",dept:{name:'xxx',emps:'...'}},
     *      ...
     *  ]
     * }
     * 也就是說(shuō),輸出結(jié)果中,部門(mén)對(duì)象dept的json串中包含員工對(duì)象emp,
     * 而員工對(duì)象emp 中又包含dept,這樣互相包含就無(wú)線(xiàn)遞歸下去,json串越來(lái)越長(zhǎng)...
     * 直到棧溢出!
     */
    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;
    }
}
/**
 * 部門(mén)
 */
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 線(xiàn)程運(yùn)行診斷

案例1:CPU占用過(guò)高

  • Linux環(huán)境下運(yùn)行某些程序的時(shí)候,可能導(dǎo)致CPU的占用過(guò)高,這時(shí)需要定位占用CPU過(guò)高的線(xiàn)程
  • top命令,查看是哪個(gè)進(jìn)程占用CPU過(guò)高

在這里插入圖片描述


在這里插入圖片描述

  • ps H -eo pid, tid(線(xiàn)程id), %cpu | grep 剛才通過(guò)top查到的進(jìn)程號(hào) 通過(guò)ps命令進(jìn)一步查看具體是哪個(gè)線(xiàn)程占用CPU過(guò)高!

在這里插入圖片描述

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

在這里插入圖片描述


在這里插入圖片描述

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

在這里插入圖片描述


3、本地方法棧

在這里插入圖片描述

在這里插入圖片描述

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

如圖:


在這里插入圖片描述


在這里插入圖片描述

  • 本地接口的作用是融合不同的編程語(yǔ)言為Java所用,它的初衷是融合C/C++程序,Java誕生的時(shí)候是C/C++橫行的時(shí)候,要想立足,必須由調(diào)用C/C++程序,于是就在內(nèi)存中專(zhuān)門(mén)開(kāi)辟了一塊區(qū)域處理標(biāo)記為native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時(shí)加載native libraies
  • 目前該方法的使用的越來(lái)越少了,除非是與硬件有關(guān)的應(yīng)用,比如通過(guò)Java程序驅(qū)動(dòng)打印機(jī)或者Java系統(tǒng)管理生產(chǎn)設(shè)備,在企業(yè)級(jí)應(yīng)用中已經(jīng)比較少見(jiàn)。因?yàn)楝F(xiàn)在的異構(gòu)領(lǐng)域間的通信很發(fā)達(dá),比如可以使用Socket通信,也可以使用Web Service等等,不多做介紹
  • 本地方法棧(Native Method Stack):(它的具體做法是Native Method Stack中登記native方法,在Execution Engine 執(zhí)行時(shí)加載本地方法庫(kù))
  • native方法的舉例: Object類(lèi)中的clone wait notify hashCode 等 Unsafe類(lèi)都是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í)了,來(lái)分享一個(gè)馬上工作需要的場(chǎng)景,要做數(shù)據(jù)的抽取,不用kettle,想用flink。實(shí)際就是flink的sql、table層級(jí)的api
    2022-05-05
  • Servlet從入門(mén)到精通(超級(jí)詳細(xì)!)

    Servlet從入門(mén)到精通(超級(jí)詳細(xì)!)

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

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

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

    java ThreadPool線(xiàn)程池的使用,線(xiàn)程池工具類(lèi)用法說(shuō)明

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論