一篇文章学会JVM ~~ 超详细

目录

前言

一、什么是JVM? JVM 又称 Java虚拟机? 必了解问题

二、JVM 跟 JDK、JRE的关系?

三、为什么要学习JVM? 必了解问题 

四、JVM运行时数据区

4.1 JVM运行时数据区是什么?

4.2 为什么要学习JVM运行时数据区?

五、程序计数器

六、虚拟机栈

七、本地方法栈

八、提几个问题

八、方法区

九、heap:分代模型


前言

罗里吧嗦一大堆,建议大家可以看一下,从不懂JVM到初步了解JVM的感受。

最近去华为面试,被面试官问到了:“了解java的JVM吗?能不能详细跟我介绍这个JVM“。,在面试之前,我也在网上搜索一下关于JVM相关面试问题,当初的回答是非常勉强,实际上关于JVM很多概念的理解都只是停留背诵的层面。比如说:JVM为什么会有程序计数器?

答:程序计数器是用来记忆住线程执行指令的指针,之所以要有程序计数器是因为线程存在挂起的现象,线程拿到CPU执行权之后,执行到某个指令,假如突然失去CPU的执行权(这跟操作系统中CPU的算法有关,随机轮询、线程优先级算法等),那么该线程就会被挂起。此时等到下次重新拿到CPU的执行权时,那么该线程会继续往下面执行。这是就需要有一个记忆该线程上次执行到哪一个指令的指针,因此就会有程序计数器这个概念。

当时回答也没有这个详细,只是粗略回答了几句,然后对方也很失望,不再深究了。之后就思考一个问题:JVM相关的知识,在实际开发中确实是很少用到,为什么在大公司经常会问这样的问题?仅仅是为了面试吗?答案肯定不是这样的。这个问题的答案在下面的文章中可以找到。111

此篇文章纯粹是来自网上某关于JVM讲解的视频。该视频是我看过讲解JVM最好的视频 ~~ 特此将我学到的相关内容整理成下面这篇文章。

目前只有视频的一半内容,后续有完整的将会继续更新,觉得博主写的不错的,可以收藏一下哦!

绝对适合应届的、从事Java开发有1~5年工作经验的 ~~ 

一、什么是JVM? JVM 又称 Java虚拟机? 必了解问题

网上的回答:java虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。

上面的回答比较抽象,我用简单的例子来说明介绍JVM,以及给出个人对JVM的理解。

1、小郑同学编写一个HelloWorld.java文件

2、HelloWorld.java经过javac编译之后生成HelloWorld.class文件

3、想要执行该文件,就要转化成操作系统能够认识的指令去执行。

4、这时候,就有了JVM,它能够帮助我们将编译文件转化成操作能够认识的指令。

上述的步骤,看下图:

JVM就是从软件层面,屏蔽底层硬件、指令层面的细节,根据当前的机器环境情况将编译后的文件转化成机器能够识别的指令

面试题一:Java语言为什么能够跨平台?

Java比较流行的一个原因是跨平台 ~~ Write Once Run Everywhere 。这是因为有JVM的存在,操作系统不一样它的指令有所不同,JVM能根据不同的操作系统转化成该操作系统能够识别的指令。

二、JVM 跟 JDK、JRE的关系?

JDK: java Development Kit

JRE:Java Runtime Enviroment

JVM:Java Virtual Machine

JDK包含JDK + 其他工具

JDK包含JRE + 其他工具

所以JDK里面也有JVM

看图:

三、为什么要学习JVM? 必了解问题 

答案:内存管理。

在C与C++中,新创建一个变量是要手动去申请空间,释放空间,而在Java中把内存控制的权力交给了Java虚拟机。所以平时在开发过程中就不需要再去关注对象、变量的内存管理。

看起来由虚拟机管理内存一切都很美好。不过,也正是因为Java程序员把内存控制的权力交给了Java虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误将会成为一项异常艰难的工作。

因此,在大公司的面试中,JVM是经常问到的问题。【正方面去考虑问题,JVM好处部分,也要考虑坏处部分】

四、JVM运行时数据区

4.1 JVM运行时数据区是什么?

包括五个部分:

指令:程序计数器、虚拟机栈、本地方法栈

数据:方法区、Head

【注意:】以下是以JDK1.6为例子,在后续的版本可能会有点不一样。

这里关于这五个部分的详细介绍暂时不作详细说明,后面会对每各部分详细说明

4.2 为什么要学习JVM运行时数据区?

举个例子:人喝水,经过食道、胃、大肠,然后大肠吸收水成H2O,然后分解提供能量。如果想要研究水怎么分解提供能量的,就必须了解这一系列的过程。

同理,那么一个类文件在JVM运行中,成员变量、静态变量、局部变量等究竟存储在哪里!而且堆和栈又是如何去引用这些变量。这也就是我们为什么要去学习运行时数据库的原因,只要了解这一系列的过程,才能更好的去理解JVM。

五、程序计数器

【what】程序计数器:指向当前线程正在执行的字节码指令的地址(行号)

面试题一:Java中最小的执行单位是什么?

答案:线程

面试题二:为什么要记录当前线程正在执行的指令? 当前指令执行不就完了吗?为什么还要记录呢?

举个小小例子 + 图示理解

1、线程A拿到CPU执行权,正在执行该线程。

2、出现线程B,由于线程B抢占了CPU的执行权,此时线程A挂起。(关于CPU的算法比较复杂,时间片、非抢占、抢占式等等,这里不展开讲)

3、当线程B执行完之后,线程A拿到CPU执行权,需要继续往下执行。线程本身是不存储数据的,因此它需要去属于我的程序计数器去拿我当前正在执行的指令往下面执行。

4、这就是为什么要有程序技术器的原因。【也是我开头问题的答案哦】

【学习技巧:】利用我们所掌握的知识,去推导我们未知的领域。

六、虚拟机栈

【what】虚拟机栈:存储当前线程运行方法时所需要的数据,指令、返回地址。

举个例子 + 图示理解虚拟机栈:

1、虚拟机栈:栈 --》数据结构 ---》先进先出,用一个黄色长矩形表示。

2、栈内放东西,因此需要一个栈帧压入数据,用绿色长矩形表示。

3、栈帧里面有:局部变量表、操作数栈、动态链接、出口等等。用白色矩形表示。

6.1 局部变量表:存储局部变量的表。在我们编译期就能够确定大小。

局部变量表为32位的物理空间,故能放在一个int类型局部变量,如果是float类型,则要分开存放。

结核操作数栈理解。

问题一:栈指向堆的理解?

栈:指的是局部变量表。局部变量表存储的局部变量 ~~ 基本类型 + 引用类型。

如果是引用类型,所有的对象本身存放在堆中,因此会栈会指向堆。

当然,也可以通过句柄池,然后在执行堆

看图:

 

6.2 操作数栈:是用来执行操作的地方。

1、反编译.class文件,查看底层代码是如何实现的

DugtTest.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.dgut.edu.cn;

import java.io.File;

public class DugtTest {
    private final int i = 0;
    private static int k = 0;
    private Object obj = new Object();
    private int sss = 0;

    public DugtTest() {
    }

    public void methodOne(int i) {
        int j = 0;
        int var10000 = i + j;
        Object acb = this.obj;
        long start = System.currentTimeMillis();
        this.methodTwo();
    }

    public void methodTwo() {
        new File("");
    }

    public static void methodThree() {
        methodThree();
    }

    public void m() {
    }

    public static void main(String[] args) {
        System.out.println("=====123456=====");
    }
}

执行main方法,将会编译该文件,生成DugtTest.class文件。

【注意:】我在eclipse中,不生成编译文件,需要另外配置,但是在IDEA中就可以。

在命令行中,执行下面指令生成反编译文件。

 javap -v DugtTest.class > 20181209.txt

以下便是编译后的文件。

2、javap指令集

详细指令参考该文章:https://www.cnblogs.com/JsonShare/p/8798735.html

3、理解编译文件

4、图标理解:

问题一:为什么j=0存入到局部变量表2的位置,而不是1的位置?

答:public void methodOne(int i) {},将i存入到局部变量表1。因此j存放入局部变量表2。

验证过程:

int var10000 = i + j 。

首先 加载局部变量1(iload_1),然后加载局部变量2(iload_2),然后执行iadd操作,将结果集压入到局部变量表3的位置。 

问题二:局部变量表0的位置存储的是什么?

如果说是成员方法,存储的是this。如果说是静态方法,则没有this。

因此:操作栈是用来执行操作的地方。

6.3 动态链接:运行时多态

举个例子 + 图示 理解

1、常见@Autowired注入一个userService

2、在方法中调用userService.do()方法

3、jvm事先不知道userService.do()执行是哪个方法,因此需要进行解析,将userService解析成具体的ServiceA还是ServiceB,然后执行。

而动态链接就是专门做这个解析的

问题一:动态链接为什么要放在栈帧里面?

答案:一个方法一个栈帧,因为只有在方法中才会去调用动态链接解释,因此它需要放在栈帧里面。

6.4 出口:方法结束之后,栈帧需要出栈。

return;出口

有两种情况,一种是正常结束,return。另一种是出现异常,我们可以抛出异常或者是try..catch..

七、本地方法栈

类似的:

虚拟机栈 ---》 运行JAVA方法的一套方式

本地方法栈 ---》 运行本地方法的一套方式

什么叫本地方法?使用native修饰的方法

这里不做详细说明,都差不多的

八、提几个问题

问题一:一个方法一个栈帧,当方法嵌套调用怎么办呢?methodOne调用methodTwo

回答:就压栈,一个操作栈里面有两个栈帧。这时候,methodTwo在上面,methodOne在下面。

原因:methodOne调用methodTwo,可能会需要用到methodTwo返回结果值,因此需要先执行methodTwo。

问题二:一个方法一个栈帧,如果是递归调用的话,那么有多少个栈帧?

回答:N个栈帧

证明:递归调用,不断递归会出现栈溢出StackOverflowError,表示不断创建栈帧,超过了操作栈本身的内存大小,故报内存溢出。

问题三:数据共享逻辑图,线程安全问题解析?

【只是逻辑:】如果说线程A与线程B都有各自的程序计数器,虚拟机栈,本地方法栈,那么就不存在线程安全问题。只有说两个线程公用某个公用的部分,那么就会存在线程安全问题。【在线程安全篇会详细分析】

八、方法区

方法区:存放类信息(元数据)、常量(1.7+有变化)、静态变量、JIT(1.7以前)。也是我们俗称的老年代。

【注意】在1.7版本之后,将我们的常量迁移到了head这边。

问题一:解释为什么成员变量线程不安全的?

回答:因为方法区、堆被线程所共享。

九、heap:分代模型

关于head详细讲解,这里推荐一篇写得很好的文章 ~~ 个人理解分代模型,也是通过下面这篇文章来理解的~~

https://blog.csdn.net/kefengwang/article/details/54378235

问题一:young gc的垃圾回收算法是什么?

回答:复制回收算法

十、引发后续问题~~

1、回收算法   理论

      垃圾回收器   理论具体实现

2、对象什么时候被回收?

回答:成为GC Root

什么对象可以成为GC ROOT?

为什么他们可以成为GC ROOT?

当对象没有任何引用的时候,那么它就可以成为GC ROOT,但不是立刻就回收,而是等到调用GC时候进行回收

3、如何用MAT分析dump文件定位内存泄漏?

这一部分涉及到实战部分,后续会更新该文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值