内存区域划分

虚拟机运行时数据区

程序计数器

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程直接计数器互不影响,独立储存。

特点

是否是线程私有的

异常

不会发生OOM

执行java方法

记录正在执行的字节码指令的行号

执行native方法

Undefinde

虚拟机栈

虚拟机栈是一个后入先出的栈。栈帧是保存在虚拟机栈中的,栈帧是用来存储数据和存储部分过程结果的数据结构。线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素。

栈帧

栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈的栈元素。栈帧存储了方法的局部变量表,操作数栈,动态连接和方法返回地址等信息。第一个方法从调用开始到执行完成,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

局部变量表

局部变量表存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。 局部变量表的容量以变量槽Slot为最小单位。默认存储32位的类型数据,如果是64位,则分配连续两个槽。

操作数栈

操作数栈也常被称为操作栈,它是一个后入先出栈。 当一个方法刚刚执行的时候,这个方法的操作数栈是空的,在方法执行的过程中,会有各种字节码指向操作数栈中写入和提取值,也就是入栈与出栈操作。

动态链接

每个栈帧都包含一个指向运行时常量池中该栈帧所属性方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。

方法返回地址

1、正常完成出口: 执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者(调用当前方法的的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定。

2、 异常完成出口:在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出, 是不会给它的调用都产生任何返回值的。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则文虚拟机使用到的Native方法服务。

Java堆是被所以线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。Java堆是垃圾收集器管理的主要区域。从内存回收的角度来看,由于现在的收集器都采用分代收集算法,所以Java对中还可以细分为:新生代老年代;新生代有分有Eden、From Survivor、To Survivor等。

方法区

方法区也是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区又被称为永久代

运行时常量池

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生产的工作字面量和符号的引用,这部分内容将在类加载之后进入方法区中的运行时常量池。

内存分配策略

对象优先在Eden分配

大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

大对象直接进入老年代

所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配。

长期存活的对象将进入老年代

虚拟机给每个对象定义了一个对象年龄计数器。如果对象在Eden出生并经过第一次 Minor GC后仍然存活,并且能被 Survivor容纳的话,将被移动到 Survivor空间中,并且对象年龄设为1.对象在 Survivor区中每“熬过”一次 Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenringThreshold设置。

Last updated