目录
运行时数据区(Run-Time Data Areas)
官网概括
图解
常规理解
方法区(Method Area)
堆(Heap)
虚拟机栈(Java Virtual Machine Stacks)
程序计数器(PC Register)
本地方法栈(Native Method Stacks)
结合字节码指令理解虚拟机栈和栈帧
指向关系
栈指向堆
方法区指向堆
堆指向方法区
Java对象内存布局
运行时数据区(Run-Time Data Areas)
在装载的第(2)(3)步有听到运行时数据,堆,方法去等名词
(2)
将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
(3)
在
Java
堆中生成一个代表这个类的
java.lang.Class
对象,作为对方法区中这些数据的访问入口
说白了就是类文件被类装载器装载进来之后,类中的内容
(
比如变量,常量,方法,对象等这些数
据得要有个去处,也就是要存储起来,存储的位置肯定是在
JVM
中有对应的空间
)
官网概括
The Java Virtual Machine defines various run-time data areas that are used during execution of a program. Some of these data areas are created on Java Virtual Machine start-up and are destroyed only when the Java Virtual Machine exits. Other data areas are per thread. Per-thread data areas are created when a thread is created and destroyed when the thread exits.
图解
常规理解
方法区(Method Area)
- 方法区是各个线程共享的内存区域,在虚拟机启动时创建。
- 用于存储已被虚拟机加载的类息、常量、静态变量、即时编译器编译后的代码等数据。
- 虽然Java虚拟机规范把方法去描述为堆的一个逻辑部分,但是它却又一个别名No-Heap,自然是与Java堆分开来的。
- 当方法区无法满足分配需求时,将抛出OutOfMemoryError异常。
此时我们回看装载阶段的第二步;(2)将这个字节流锁代表的静态存储结构转化为方法去的运行时数据结构
如果这时候把从
Class
文件到装载的第
(1)
和
(2)
步合并起来理解的话,可
以画个图
需要说明的:
堆(Heap)
(1)堆是Java虚拟机锁管理的内存中最大的一块,在虚拟机启动时创建,被所有线程共享
(2)Java对象实例以及数组都在堆上分配
此时回看装载阶段的第
3
步:
(3)
在
Java
堆中生成一个代表这个类的
java.lang.Class
对象,作为对方
法区中这些数据的访问入口
此时装载
(1)(2)(3)
的图可以改动一下
虚拟机栈(Java Virtual Machine Stacks)
经过上面的分析,类加载机制的装载过程已经完成,后续的链接,初始化也会相应的生效。
假如目前的阶段是初始化完成了,后续做啥呢?肯定是
Use
咯,不用的话这样折腾来折腾去
有什么意义?那怎样才能被使用到?换句话说里面内容怎样才能被执行?比如通过主函数
main
调
用其他方法,这种方式实际上是
main
线程执行之后调用的方法,即要想使用里面的各种内容,得
要以线程为单位,执行相应的方法才行。
那一个线程执行的状态如何维护?一个线程可以执行多少个方法?这样的关系怎么维护呢?
(1)虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态,换句话说,一个Java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈肯定是线程私有的,独有的,随着线程的创建而创建。
(2)每一个被线程执行的方法,为该栈中的栈帧,即每一个方法对应一个栈帧。
(3)调用一个方法,就会
向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。
写段伪代码
/
画图理解栈和栈帧
程序计数器(PC Register)
我们都知道一个
JVM
进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据
CPU
调度来的。
假如线程
A
正在执行到某个地方,突然失去了
CPU
的执行权,切换到线程
B
了,然后当线程
A
再获
得
CPU
执行权的时候,怎么能继续执行呢?这就是需要在线程中维护一个变量,记录线程执行到
的位置。
(1)程序技术其占用的内存空间很小,由于Java虚拟机的多线程是通过线程轮流切换,并分配处理器执行时间的方式来实现的,在任意时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的位置,每条线程需要在一个独立的程序计数器(线程私有)。
(2)如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址。
(3)如果正在执行的是native方法,则计数器为空。
本地方法栈(Native Method Stacks)
如果当前线程执行的方法时Native类型的,这些方法就会在本地方法栈中执行。
结合字节码指令理解虚拟机栈和栈帧
栈帧:每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间。
每个栈帧中包括局部变量表
(Local Variables)
、操作数栈
(Operand Stack)
、指向运行时常量池的引用
(A
reference to the run-time constant pool)
、方法返回地址
(Return Address)
和附加信息。
指向关系
栈指向堆
如果在栈帧中有一个变量,类型为引用类型,比如
Object obj=new Object()
,这时候就是典型的栈中元
素指向堆中的对象。
方法区指向堆
方法区中会存放静态变量,常量等数据。如果是下面这种情况,就是典型的方法区中元素指向堆中的对
象。
堆指向方法区
注意,方法区中会包含类的信息,堆中会有对象,那怎么知道对象是哪个类创建的呢?
Java对象内存布局
一个Java对象在内存中包括3个部分:对象头、实例数据和对齐填充