无法解析的外部符号main_JVM内部结构深度解析

本文深入解析Java虚拟机(JVM)的内部结构,包括线程栈、程序计数器、本地方法栈的详细内容,以及对象创建的流程,包括类加载检查、内存分配、初始化和对象头设置。讨论了线程栈的局部变量表、操作数栈、动态链接和方法出口,以及程序计数器在多线程环境中的作用。此外,还介绍了JVM的内存分配问题,包括指针碰撞、空闲列表和并发分配策略。最后,探讨了对象在堆内存中的生命周期,包括对象的初始化、内存分配、逃逸分析和垃圾回收机制。
摘要由CSDN通过智能技术生成

0f97105f66475bc6f09e2b36a9c92cb9.png

对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug,同时,JVM也是面试环节重点考察点,下面我们来了解一下JVM内部结构!

jvm内存关系如图所示:

8b1df2683ab43efab3f08050d4100d02.png

上边的图会结合Math类进行分析,Math如下所示

//Math类
public class Math {
    public static final int initData = 666;
    public static User user = new User();

    public int compute() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }

    public static void main(String[] args) {
        Math math = new Math();
        math.compute();
    }
}

1.1 Math中的main方法执行流程


①:首先Math.class由应用类加载器加载到jvm内存中。
②:在线程执行Math中的main方法时,会在jvm整个栈内存中会给当前线程分配一块内存作为当前的线程栈 空间。
③:main()方法只要一开始运行,会在当前线程的栈空间内部压入main()方法的栈帧,栈帧内用来存放main方法的局部变量,比如 math,在main方法的栈帧中存储的是Math对象的对象头地址,用来指向堆空间中Math对象的位置。
④:然后开始执行compute()方法,会在当前线程的栈空间内,压入一个compute()的栈帧(每一个方法对应一个栈帧),栈帧内部包含局部变量表、操作数栈、动态链接、方法出口等。
⑤:等compute()方法执行完毕,释放栈空间,继续执行main()方法

结论:

  1. 线程栈中每一个方法对应一个栈帧,保证每个方法内部的局部变量相互隔离。
  2. 栈帧的执行遵循先进后出原则,即先压入的main方法后执行,后压入的compute方法先执行。

1.2 线程栈内部结构解析
局部变量表:存储局部变量,如a=1,b=2,或者对象。
操作数栈:也是一个栈,用来临时存放局部变量表中的局部变量的结果值,例如执行compute方法时,先把a的值放入局部变量表,再把a的值 1 放入操作数栈,然后再把操作数栈的值1,赋给局部变量表中的a,包括 (a + b) * 10的计算也在操作数栈中进行。
动态链接:把符号引用转换为直接引用,比如执行到compute()方法时,会把这个方法的符号转换为方法区的地址。
方法出口:方法执行完毕后,回到哪一行,哪个方法的哪个位置,是方法出口来记录的。

1.3 什么是程序计数器,为什么每个线程都要包含程序计数器?
①:程序计数器是记录当前线程的执行字节码的位置。
②:字节码执行引擎在执行方法区中的字节码信息时,会记录当前执行的行号,每当线程往下执行一步,就会改变程序计数器的行号。
③:在多线程模式下,如果当前线程的cpu时间片被别的线程抢去,等他再抢回cpu时,需要记录上一步执行的位置,否则要从头开始执行,而程序计数器就提供了当前执行的位置信息,所以每个线程都有自己的程序计数器。

1.4 本地方法栈

因为java的底层是C++,如果线程在执行过程中调用了很底层的C++方法(比如Thread.start

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值