理解jvm(一)--内存模型

    此文以hotspot虚拟机为例来介绍jvm的内存模型。先来一张图吧,经典的jvm内存模型示意图

jvm内存模型

1、堆

    堆是用来存储对象的内存区域,被所有线程共享,java程序运行时的对象都在堆中生存。堆也是jvm内存中最大区域。堆又被分为新生代和老年代,其中新生代又分为eden区和两个Sruvivor区。eden区是所有刚创建的对象的出生地,两个Survivor去的大小是相同的,主要存放存活较久的对象(至少经过一次gc而没有被回收的对象)。当新生代里的对象经过多次垃圾(这个可以用-XX MaxTenuringThreshold设置进入老年代对象需要的年龄)回收而没有被销毁的情况下就会被移到老年代区域里。因为老年代区域相对新生代也大了很多,因为绝大部分的对象在创建后很快就没用了,在新生代发生Minor GC的时候就会被销毁,所以新生代同时也比老年代更频繁的发生gc。老年代发生gc称为full gc,在这个过程中整个jvm停摆,所以也要尽量避免full gc,这也是老念代设置比较大的另一个原因。关于jvm垃圾回收在下片篇文章里着重介绍,本文就不做过多说明了。

2、方法区

    又叫静态区,跟堆一样,被所有的线程共享,方法区包含所有的class和static变量在hotspot的jvm1.7之前的实现中String字符串常量池也在方法区里。在hotspot中方法区在jdk1.7之前又叫永久代,在jdk1.8之后方法区已经移出了jvm内存区域,改名叫做元空间(MetaSpace),其中原永久代中的类的元数据移到了本机内存中(native memory),这样类的元数据只受本地内存大小限制,就没有oom了,原永久代中的字符串常量池移到了堆中。

3、虚拟机栈

    java虚拟机栈是java执行方法的内存模型,每一个方法从调用到调用完成对应着一个栈帧入栈都出栈的过程,一个栈帧(Stack Frame)包含局部变量表,操作数栈,动态链接,方法出口等信息。

栈帧图解模型如下。


虚拟机栈

局部变量表用于存放方法参数变量和局部变量在Class的方法表中指定了该变量表的最大容量。

操作数栈用于方法执行中进行算术运算或者是调用其他的方法进行参数传递的时候是通过操作数栈进行的。

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

返回地址顾名思义就是这个方法执行完以后要返回的指令地址,也就是调用者方法程序计数器地址。当方法返回时,可能进行3个操作恢复上层方法的局部变量表和操作数栈把返回值压入调用者调用者栈帧的操作数栈调整 PC 计数器的值以指向方法调用指令后面的一条指令。

4、程序计数器

程序计数器(program counter register)只占用了一块比较小的内存空间,至于小到什么程度呢,这样说吧,有时可以忽略不计的。

可以看作是当前线程所执行的字节码文件(class)的行号指示器。在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要这玩意来实现的,NB吗?

因为处理器在一个确定是时刻只会执行一个线程中的指令,线程切换后,是通过计数器来记录执行痕迹的,因而可以看出,程序计数器是每个线程私有的。如果执行的是java方法,那么记录的是正在执行的虚拟机字节码指令的地址的地址,如果是native方法,计数器的值为空(undefined)。这个内存区域是唯一一个在java虚拟界规范中没有规定任何OutOfMemoryError的情况的区域。至于为什么没有这个异常呢,要是一个计数的功能在出这个异常,那么我也是醉了。

你可能感兴趣的:(理解jvm(一)--内存模型)