JVM-内存模型【一篇就够】

一、JVM内存模型(JMM)

 

二、程序计数器(PC)

每个线程都会有自己私有的程序计数器(PC)。可以看作是当前线程所执行的字节码的行号指示器。也可以理解为下一条将要执行的指令的地址或者行号。字节码解释器就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程上下文切换、线程恢复时,都要依赖PC。

1)如果线程正在执行的是一个Java方法,PC值为正在执行的虚拟机字节码指令的地址

2)如果线程正在执行的是Native方法,PC值为空(未定义)

三、虚拟机栈(VM Stack)

VM Stack也是线程私有的区域。它是Java方法执行时的字典:它里面记录了局部变量表(Local Variables)、操作数栈(Operand Stack)、动态链接、方法出口等信息。

栈桢(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。

栈桢随着方法调用而创建,随着方法结束而销毁——无论方法时正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。

栈桢的存储空间分配在Java虚拟机栈之中,每一个栈桢都有自己的局部变量表、操作数栈和指向当前方法所属的类的运行时常量池的引用。

栈桢是一个栈,也是一块内存区域,所以它是有大小的。但是一般而言,各种虚拟机的实现都支持动态扩展这部分内存。

1)如果线程请求的栈深度太大,则抛出StackOverflowError

2)如果动态扩展时没有足够的大小,则抛出OutOfMemoryError

四、本地方法栈(Native Method Stack)

Java虚拟机实现可能会使用到传统的栈(通常称之为“C Stacks”)来支持native方法(指使用Java以外的其他语言编写的方法)的执行,这个栈就是本地方法栈。

VM Stack是为执行Java方法服务的,此处的Native Method Stack是为执行本地方法服务的。此处的本地方法指的是和具体的底层操作系统侧面相关的接口调用。

《Java虚拟机规范》中没有对这部分做具体的规定,所以就由VM的实现者自由发挥了。

有的虚拟机(比如HotSpot)将VM Stack和Native Method Stack合二为一,所以VM的另一种内存区域图就如下面所示了:


五、Java堆(Heap)

堆是可供各个线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。

1)Java堆在虚拟机启动的时候就被创建

2)Java堆是所有线程共享的内存区域

3)Java堆存储了被自动内存管理系统(Automatic Storage Management System,也即是常说的Garbage Collector - 垃圾收集器)所管理的各种对象,这些受管理的对象无需,也无法显式地被销毁。并未指明用什么具体的技术去实现自动内存管理系统

4)Java堆的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩

5)Java堆所使用的内存不需要保证是连续的

6)如果实际所需的堆超过了自动内存管理系统能提供的最大容量,那Java虚拟机将会抛出一个OutOfMemoryError异常

7)实现者应当提供给程序员或者最终用户调节Java堆初始容量的手段。对于 可以动态扩展和收缩的Java堆来说,则应当提供调节其最大、最小容量的手段

8)所有的对象实例以及数据都要在堆上分配


六、方法区(Method Area)

方法区是由所有线程共享的内存区域。

方法区存储的内容大致如下:

1)每一个类的结构信息(运行时常量池Runtime Constant Pool、字段和方法数据、构造函数和普通方法的字节码内容)

2)类、实例、接口初始化时用到的特殊方法

每一个运行时常量池都分配在Java虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。当创建类或接口的时候,如果构造运行时常量池所需要的内存空间超过了方法区所能提供的最大值,那Java虚拟机将会抛出一个OutOfMemoryError异常。

七、直接内存(Direct Memory)

此处的直接内存并不是由JVM管理的内存。它是利用本地方法库直接在Java堆之外申请的内存区域。比如NIO中的DirectByteBuffer就是操作直接内存的。

直接内存的好处就是避免了在Java堆和native堆直接同步数据的步骤。但它并不是由JVM来管理的。


猜你喜欢

转载自blog.csdn.net/sjmz30071360/article/details/80415715