如图:
包含五部分:堆,方法区,虚拟机栈,本地方法栈,程序计数器。
堆
堆是jvm所管理的内存中最大的一块内存区域,线程共享的内存区域,可以不使用连续的内存地址,该区域主要存放对象实例及数组。
方法区
主要用于存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据,可以不使用连续的内存地址,线程共享的内存区域。常量池是方法区的一部分。
虚拟机栈
线程私有,生命周期与线程相同,使用连续的内存空间,描述的是java方法执行的内存模型:每个方法被调用时都会创建一个对应的栈帧,方法的执行过程意味着对应的栈帧在虚拟机栈中入栈到出栈的过程。栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,栈帧由三部分组成:局部变量区,操作数栈,动态链接,方法返回地址;
局部变量表:用于存储方法参数和方法内定义的局部变量,通过索引来访问;如果是非静态方法,则在index[0]位置上存储的是方法所属对象的实例引用,随后存储的是参数和局部变量。
操作数栈:是一个后入先出栈(LIFO),通过标准的栈操作—压栈和出栈—来访问的,操作数栈的每一个元素可以是任意Java数据类型。在方法执行过程中会有各种指令往栈中写入和提取信息。JVM的执行引擎就是基于栈的执行引擎,其中的栈指的就是操作数栈。
当执行一个方法时,随着方法和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,计算完毕将结果出栈到局部变量表或返回给方法调用者,也就是出栈/入栈操作。i++ 和 ++i 区别
i++: 从局部变量表中取出 i 压入操作数栈,然后对局部变量表中的 i 自增1,将操作数栈栈顶值取出使用,所以线程从操作数栈读取到的是自增之前的值。(先放入操作数栈,再自增)
++i: 先对局部变量表的 i 自增1,然后取出并压入操作数栈,再将操作数栈栈顶值取出使用,所以,线程从操作数栈读取到的值是自增之后的值。 (先自增,再放入操作数栈)动态链接:每个栈帧中包含一个在常量池中对当前方法的引用,目的是支持方法调用过程中的动态连接。
方法返回地址:方法的执行有两种情况退出:1.正常退出,2.异常退出;无论何种情况退出都将返回到方法被调用的位置。
本地方法栈
与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。
程序计数器
最小的一块内存区域,是一个记录着当前线程所执行的字节码的行号指示器。是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域。
Java内存模型中的有序性可以总结为:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。