运行时数据区
程序计数器
虚拟机栈
虚拟机栈用于存放方法的调用信息和数据,描述的是方法的内存模型,每次方法的调用数据都是通过栈来传递的;虚拟机栈是线程私有的,相当于是线程的方法调用链
实际上,虚拟机栈是由一个个栈帧组成的,每个栈帧就表示一次方法调用,每个栈帧都有:局部变量表、操作数栈、动态链接、方法出口信息
局部变量表
局部变量表主要用于存储编译时期已知的各种数据类型(包括基本数据类型:byte、short、int、long、float、double、char、boolean和对象引用:对象的内存地址)
动态链接
当一个方法体内需要调用其它的方法时,便需要通过动态链接来获取另一个对象的目标的方法,一般这个对象的引用信息会存在运行时常量池中,通过动态链接便可以在常量池中获取这个对象的直接引用
方法出口信息
当一个方法开始执行后,一般来说有两种方法退出执行
- 正常执行完成:会将调用这个方法的计数器作为返回地址,并且将返回值压入调用者的操作数栈,然后栈帧出栈
遇到异常退出:遇到异常退出时一般来说没有返回值,通常需要通过异常处理器来确定要返回的地址
操作数栈
也成为操作栈,用于执行算术运算或者是调用其它方法传递参数或获取返回值的媒介,所以操作数栈中的元素顺序必须和字节码的指令顺序严格匹配
程序刚开始运行时,操作栈中为空;随着程序的不断运行,会有各种字节码指令不断入栈和出栈数据,可能来自本身,也可能来自调用其它方法的返回值本地方法栈
作用与虚拟机栈相似,但是调用的方法都是本地方法(Native 方法),在HotSpot中虚拟机栈和本地方法栈合二为一。
本地方法一般是由非Java语言实现的,如C、C++;用于支持一些 Java 语言无法实现的操作堆
堆内存中几乎存放了所有的对象实例,是垃圾回收的主要区域,通常被划分成三部分:新生代,老年代,永久代(JDK1.8之后被元空间取代,并被移到了直接内存中);但随着JIT编译器和逃逸分析技术的成熟,若方法中创建的对象没有返回也没有在外部引用,对象也有可能分配在栈中
- 字符串常量池在jdk1.7之前被放置在方法区中(永久代),JDK1.7之后被移动到了堆内存中
字符串常量池
字符串常量池在JDK1.7之前和运行时常量池一样放置在永久代中;JDK1.7之后移到了堆内存中
字符串如果不通过new关键字定义或者引用拼接,一般在编译期就可以被优化到字符串常量池中
String intern() 方法可以将通过new关键字创建出来的字符串在常量池中再创建一个相同的内容并返回字符串常量池中的地址
final 修饰的String对象都会被当作常量使用,在编译期间就确定的方法区
用于存储被JVM加载的类信息、常量、静态常量、即时编译后的代码等数据
方法区其实只是一个标准、规范,不同的虚拟机有不同的实现,如永久代、元空间永久代
permanent,JDK1.8之前方法区的实现是永久代,是堆的一个逻辑部分,共用堆内存,
一般垃圾收集很少在永久代工作元空间
meta space,JDK1.8以及之后的方法区的实现是元空间,并且使用的是直接内存,不再受堆大小的限制;但如果不限制元空间大小的话,可能会耗尽系统内存运行时常量池
运行时常量池是方法区的一部分,用于存放一些符号引用和静态常量,动态链接的使用就会用到运行时常量池中的符号引用