java架构之道-JVM内存模型

整体介绍

每当我们去面试时,面试官经常会问jvm相关的问题,谈到JVM,首先不得不说jvm内存模型,今天就来深度剖析一下它,jvm内存模型的结构图如下:

对于jvm内存模型为何要如此设计,下面给你解释一下java程序的运行过程你就明白了:

所谓JVM内存模型实际上的意思是java运行时数据区域,它整个过程就是当程序要执行某一段代码时,类加载器加载我们的class字节码文件,把读取的信息翻译成类信息存放到我们的方法区,同时在堆中生成该类的Class对象,当我们程序运行调用方法时,局部变量、对象引用、数组引用会在虚拟机栈中生成,如果在方法调用中需要new对象,则会在堆中根据Class信息生成对象,最后由我们的字节码执行引擎解释执行方法区的代码块,就完成了我们的代码执行。当然这个过程中,涉及到线程的切换,这时程序计数器就派上用场了,它会记录当前线程执行到哪一行代码,下次该线程再次获取cpu执行权时,继续从该行代码继续执行,最后本地方法栈和虚拟机栈的功能几乎一模一样,只不过它是执行的native本地方法,底层是调用的C或C++代码。

这段话其实就是JVM内存模型的整个执行流程,有点干,没事,我后面会做详细分析,读者只需要大概读一下有个整体印象就行,等我分析完了你再回头来看就会豁然开朗!

注意我会从各个区域使用到的先后顺序来分析,以便读者能够把它们串联起来

各个区域详解

方法区

方法区是存放从class文件加载出来的类信息的,包括常量、静态变量、类信息和运行时常量池。这里有个注意点必须给大家提醒一下,那就是该区域并不会存放Class对象,Class对象是存放在堆中的,方法区存放的类信息是一些代码片段(类似于C语言中的结构体),供我们程序调用时字节码执行引擎解释执行的。

对于方法区在JDK8以前的实现是永久代,之后是元空间,这个网上一搜一大把,我就不多废话了。

虚拟机栈

虚拟机栈是我们进行方法调用的地方,每当一个线程执行代码时,jvm就会为该线程开辟一个私有的栈空间,以下面的代码为例:

public class Math {
    public static void main(String[] args) {
        int a = 8;
        add(a);
        System.out.println(a);
    }

    public static int add(int a){
        a = a++;
        return a;
    }
}

你知道输出结果是多少吗?我相信不是回答8就是回答9,那么事实是什么呢?且听我慢慢分析

代码执行时栈的结构如下:

在java程序执行时,每调用一次方法,都会创建一个栈帧,栈帧的结构包括局部变量表,操作数栈,动态链接,方法出口,main方法执行时会创建一个栈帧,然后main方法调用add方法创建另外一个栈帧,那么add方法在执行时底层结构是什么样的呢?这时我们需要用javap -c命令反编译我们的Math.class文件,结果如下:

 

我们来对照着代码逐一分析这几句字节码指令:

 

iload_0:加载局部变量表中a的值(为8)到操作数栈,注意这时还没有进行++操作,所以此时操作数栈中a的值为8

iinc:对局部变量表中a进行++操作,所以局部变量表中的a值变为9

istore_0:将操作数中的a值赋给局部变量表中的a值,所以此时局部变量表中的a值又变为8

所以最后的答案是8

至于动态链接和方法返回出口有不懂的可以百度一下,网上很多资料,就不再累述了。

java堆

这个区域是java程序员可以控制最多的地方,我们系统新建的对象大部分都在这里分配内存空间,堆的结构如下:

 

堆由老年代和新生代组成,新建的对象一般都是在存放在Eden区,当Eden区不够用时,就会就行Minior GC,回收垃圾对象释放内存空间,当垃圾回收后任然存活的对象就会放到S0和S1其中一块区域,当再次发生Minior GC时,会把Eden区和有对象的S区中的存活对象移动到另外一块空的S区,这时先前有对象的S区又变成空的S区,所以S0和S1是相互转化的,当S区对象达到一定的阈值时,新生代的对象就会向老年代转移,当老年代的对象达到一定阈值时,就会触发Full GC

本地方法栈

和虚拟机栈的功能几乎一模一样,由于底层是C或C++实现的,我在这里就不过多介绍了(其实是不会)。

程序计数器

用于记录线程执行到哪一行代码,还记得我们上面反编译后得到的字节码指令吗?读者可以认为是字节码中每行指令前面的行号,也就是地址:

 

各个区域配置参数

 

各个区域参数配置图中已标好,记不住时读者可以来这里翻阅查找,我想说的一点是你们知道为啥是-X来设置参数吗?而有些时候却要用-D来设置参数,没错,-X表示参数的调用解析方是C或C++代码,-D表示参数的调用解析方是java代码。

更多精彩内容请关注同名公众号:java时光 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值