java虚拟机管理这块内存,所以我们也叫运行时数据区域
总览
这里按线程是否共享来分类,所谓线程不共享就是每个线程里面都会配一套 程序计数器 栈, 互相不干涉。
而方法区和堆是线程所有共享 意味着只有一个(这里注意堆是实际概念 方法区是一个虚拟概念)
程序计数器
注意:程序计数器记录字节码的内存地址,这个内存地址是怎么来的?
在前几篇中 字节码通过 .class文件 由类加载器 加载到方法区中(在解析那一步 将引用地址替换成堆内存里面的内存地址)
如下:
字节码中每一行都用地址标识,程序计数器就像是一个地址指示器,告诉执行程序 下一行去执行哪里。
java虚拟机的多线程是线程轮流切换 并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,处理器只会执行一条线程中的指令。 所以 为了线程切换后能恢复到正常位置 每个线程都需要一个独立的程序计数器,这就是为什么 程序计数器不能像堆 方法区那样 多线程共享
这篇博客的读者 一定要理解透这一点。
栈
java虚拟机栈
栈这个数据结构是个单向的,你可以把它想象成一个竖着的一边开口的箱子。它是先进后出。
看上图的Demo: main 调用a , a调用b ,b调用c。
那么在栈里面就是 mian 先入栈,后面依次是 a b c 入栈, 然后c先被执行完 执行完之后 c 出栈,后面 b a main 依次出栈。
这个时候新手可能会蒙蔽一下:a 调用 b 不是a先执行吗? 这里要注意,执行b方法 这个过程是 执行a方法中的一部分,a调用b的时候a开始执行 但是没执行完 b执行完之后 还在a的方法里面 再往下走 a才执行完
上面就是方法执行过程中栈的逻辑,每执行一个方法 就是一个栈内部栈帧入栈和出栈的过程。
然后我们来学习一下 java虚拟机栈内部一个栈帧由哪些组成:
局部变量表:
局部变量表里面存放着,运行过程中所有的局部变量,我们知道 每执行一个方法就会创建一个栈帧,那么这个方法里面的局部变量自然要存入栈帧里面,所以在栈帧里面就创建一个局部变量表来专门存储这些局部变量,方法结束 栈帧也就出栈结束了。
顺便说一句 一个方法一般都会有 方法参数,参数变量也会存在这里。
操作数栈:
操作数栈你可以把它理解成一个临时存放数据的区域,虚拟机在执行字节码指令的时候 需要把一些数据临时存起来备用,
比如我们交换 a和 b的值 在计算机里面 你需要把a的值先临时放到某个地方 然后把b的值给a 再从那个临时存放点 把a的值拿出来给b
这种执行指令时留的一点余地 就是操作数栈的作用
帧数据:
帧数据不同的虚拟机有差异
这里面最主要的需要了解的两个:
- 动态链接:在字节码指令中,一个类可能需要调用其他类的方法或者属性,这时候就需要用动态链接 用一个映射把其他类的方法信息引进来
- 方法出口:上面说过一个方法执行完 就是一个栈弹出的过程,但是弹出以后 虚拟机会疑惑 此时线程执行到哪里了?? 这里就需要在方法出口中记录方法执行完后 下一步的位置信息。
本地方法栈
本地方法栈和虚拟机栈发挥的作用很相似,只不过虚拟栈针对的是java代码编译后的字节码
本地方法栈针对的是 虚拟机本身的底层已经编写好的 native 方法。