JVM内存结构
概述
JVM能跨计算机体系结构来执行Java字节码,主要是由于JVM屏蔽了与各个计算机平台相关的软件或者硬件之间的差异,使得与平台相关的耦合统一由JVM提供者来实现。
JVM体系结构
以计算为中心来看计算机的体系结构可以分为以下几部分:
- 指令集,这个计算机所能识别的机器语言的命令集合。
- 计算单元,即能识别并且控制指令执行的功能模块。
- 寻址方式,地址的位数、最小地址和最大地址范围,以及地址的运行规则。
- 寄存器定义,包括操作数寄存器、变址寄存器,控制寄存器等的定义、数量和使用方式。
- 存储单元,能够存储操作数和保存操作结构的单元,如内核级缓存、内存和磁盘等。
JVM和实体机器的体系结构有点相似,主要由以下几部分组成:
- JVM字节码指令集
- 类加载器(在JVM启动时或者类运行时将需要的class加载到内存中)
- 执行引擎(执行引擎的任务是负责执行class文件中包含的字节码指令,相当于实体机器上的CPU)
- 内存区(将内存划分为若干个区以模拟实际机器上的存储、记录和调度功能模块,如实际机器上的各种功能寄存器或者PC指针的记录器)
- 本地方法调用(调用C或C++实现的本地方法的代码返回结果)
JVM工作体制
通常一个程序从编写到执行会经历以下阶段:
源代码 -> 预处理器 -> 编译器 -> 汇编程序 -> 源目标代码 -> 链接器 -> 可执行程序
JVM为何选择基于栈的架构
实际上,一个程序的执行,就是机器代码0和1之间的运算。而不管何种指令集都只有几种基本的元素 + - * /
而这些操作又通过指令来完成,而指令的核心目的就是确定需要的操作数和操作码,以及从哪里(寄存器或栈)获取数据,将运算结果存放到什么地方(寄存器或栈)。
事实上,不同的指令都有对应的架构来实现。如基于栈的架构实现或者基于寄存器的架构实现,这里所说的基于栈或基于寄存器都是指在一个指令中的操作数是如何存取的。
那JVM为什么选择基于栈的架构呢?
- JVM要设计与平台无关的,而平台无关性就是要保证在没有或很少的寄存器的机器上都能正确的执行java代码。
- 指令的紧凑性。??不是很明白
执行引擎的架构设计
每当创建一个新的线程时,JVM会为这个线程创建一个java栈,同时会为这个线程分配一个PC寄存器,并且这个PC寄存器指向这个线程的第一行可执行代码。这个栈帧会保留这个方法的一些元信息,如在这个方法中定义的局部变量、一些用来支持常量池的解析、正常方法返回和异常处理执行等。
JVM内存管理
扩展:
物理内存
虚拟内存
内核空间
用户空间
java虚拟机规范将运行时数据分为6种:
- PC寄存器数据
- java栈
- 堆
- 方法区
- 本地方法区
- 运行时常量池
PC寄存器数据
PC寄存器用于保存当前线程正常执行的程序的地址。java程序是多线程执行的,所以不能一直都是以线性方式执行下去,当有多个线程交叉执行时,被中断的线程的程序地址必然要保存下来,以便也它被恢复执行时再按照被中断时的指令地址继续执行下去。
java栈
每当创建一个新的线程时,JVM会为这个线程创建一个java栈。在java栈中,每执行一个方法,就会在java栈中创建一个栈帧,它会包含一些内部变量(在方法内定义的变量)、操作栈和方法返回值等信息。
每当一个方法执行完成之后,这个栈帧就会弹出栈帧的元素作为这个方法的返回值,并清除这个栈帧,java栈栈顶的栈帧就是当前正在执行的活动栈,也是正在执行的方法,PC寄存器会指向这个方法。
堆
堆是储存java对象的地方,它是JVM管理java对象的核心存取区域。每一个储存在堆中的对象都是这个对象的类的一个副本,它会复制包括继承自它父类中的所有非静态属性。
堆是被所有java线程所共享的,访问需要同步,方法和属性都需要考虑一致性问题。
方法区
JVM方法区是存储类结构信息的地方,将一个class文件解析成JVM能识别的几个部分,这些部分在class被加载到jvm时,会被储存在不同的数据结构中,其中的常量池、域、方法数据、方法体、构造函数,包括类中的专用方法。实例初始化、接口初始化都存储在这个区域。
本地方法区
本地方法区是加载native方法的地方,结构和工作方式和java栈类似。
扩展
JVM相关面试题
- 哪些内存需要回收
当堆内存中对象不被根对象直接或间接引用时,该内存需要回收。
根对象包含以下:方法中局部变量区的对象引用 虚拟机栈中的对象引用 常量池中的对象引用 本地方法栈中的对象引用
- 什么时候回收
如何回收
什么情况会出现Full GC,什么情况会出现young GC。
垃圾回收解决三个核心问题:哪些内存需要回收?什么时候回收?如何回收?
JVM内存模型
Java运行时数据区
事务的实现原理
对象分配规则。
- 新创建的对象
- jvm内存结构
程序计数器
主要用来记录当前执行指令的地址,由于java多线程执行,所以执行顺序可能不是有序的,需要有个能记录当前线程目前执行指令的地址,以确保恢复之后再按照被中断时的指令地址继续执行下去。虚拟机栈
每个方法执行时都会同时创建一个栈帧,用来存储局部变量、操作栈、动态链接、方法返回值等信息,线程私有。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程,当栈区内存不足创建栈帧时,会抛出 StackOverFlowError。本地方法栈
运行Native方法所需要的内存空间,运行过程和虚拟机栈类似。堆
JVM用来存储对象的地方,是JVM管理对象的核心区域,堆被所有java线程共享,当堆内存不足以存储对象时,抛出 OutOfMemoryError。运行时常量池
存储 class 在编译时的数字常量、方法或域的引用。- 方法区
JVM用来存储 class 的数据结构,类结构信息的地方,其中常量池、域、方法数据、构造函数等都存储在这个区域。
总结
以图结尾: