深入理解JVM(二)JVM运行时数据区域

JVM 整体架构

在这里插入图片描述
JVM总体由三部分组成:类装载子系统、运行时数据区域、字节码执行引擎。

  1. Math.java文件经Javac 编译成字节码文件Math.class
  2. 字节码文件类经类装载子系统加载(从硬盘读取到内存的过程)到运行时数据区的方法区,再由字节码执行引擎执行。
    在这里插入图片描述

运行时数据区域包括

1. 程序计数器(Program Counter Register)

程序计数器是一块较小的内存空间,可以看成当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变程序计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转异常处理、线程恢复都依赖于这个计数器。
为了使得线程切换后还能恢复到正确的执行位置,每个线程都有自己的一个计数器,各个线程之间的计数器互不影响,这类内存区域被称为“线程私有”的内存
这个程序计数器记录的是正在执行的虚拟机字节码指令的地址,但是如果是native方法,则程序计数器的值为空(undefined)。
此内存区域是唯一一个在Java虚拟规范中没有规定任何OutOfMemoryError情况的区域。

2. Java 虚拟机栈(Java virtual Machine Stacks)

与程序计数器一样,该空间也是线程私有的,其生命周期与线程相同。虚拟机栈描述的是Java方法执行时的内存模型:每个方法执行时都会创建一个栈帧,该栈帧中存储方法的**局部变量表、操作数栈、方法的出口、动态链接等等信息。**每个方法从调用到执行结束就对应着一个栈帧在虚拟机栈的入栈和出栈。
如果线程请求的栈深度大于虚拟允许的深度(递归调用方法太多时),将抛出 StakOverflowError异常。
如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

栈帧

每个方法执行时都会创建一个栈帧,该栈帧中存储方法的**局部变量表、操作数栈、方法的出口、动态链接等等信息。
在这里插入图片描述

局部变量表和操作数栈:
局部变量表存放的是一个方法的局部变量,该变量可以是基本类型的也可以是引用类型的。操作数据栈则存放着该局部 变量的对应的值(如果是引用类型则是对象对应的内存地址)。
在这里插入图片描述

动态链接

Java的多态就是使用的动态链接,父类对象指向子类引用,该父类符号引用会在每次运行时发生一次转换,转换为不同的地址。

在这里插入图片描述

3. 本地方法栈(Native Method Stack)

与Java虚拟栈类似,不过是为虚拟机使用到的Native方法服务的。与Java虚拟栈一样,该栈区域也会抛出OutOfMemoryError和Stack OverflowError异常。

4. Java堆(Java Heap)

此内存区域的唯一目的就是存放实例对象

如果在堆中没有内存完成实例分配,并且堆也无法再扩展,将会抛出OutOfMemoryError异常。

  1. 堆存放实例(包括数组、对象)。几乎所有的对象实例以及数组都要在堆上分配。
  2. Java堆是Java虚拟机中所管理内存最大的一块区域,在JVM启动时就已经创建了。
  3. 堆是GC的主要管理区域。
  4. Java堆是所有线程共享的,但在堆内部可以划分出多个线程私有的缓冲区。
  5. 根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的存储空间,只要逻辑上是连续的即可。
    在这里插入图片描述

新生代堆和老生代对

根据生命周期长短和对象大小,可以将堆可以分为新生代堆和老生代堆。两种堆的大小比例是1:2
新生代总包含eden、s0、s1,其比例为8:1:1.新生代的使用率一般在90%。在使用时,只能使用一个eden和一块S区(s0或s1)。如下图:

在这里插入图片描述
新生代中的对象的特点:

  1. 存放生命周期比较短的对象
  2. 小的对象
    判定新、老生代的对象大小临界点可以通过参数设置 -XX:PretenureSizeThredshold。一般而言,大对象一般是集合、数组、字符串。
    生命周期可以使用 -XX:MaxTenuringThredshold

老生代中的对象的特点:

  1. 生命周期较长的对象
  2. 大的对象
  3. 使用的回收器是MajorGC或FullGC

在这里插入图片描述
新生代和老生代的转化
MinorGC回收新生代中的对象。如果eden区中的对象在一次回收后仍然存活,就会被转移到s区。之后,如果MinorGC再次回收,已经在s区的对象仍然存活则年龄加一,否则被回收。如果年龄增长至设定的临界点,则对象会被转移到老生代中。

在使用时,只能使用一个eden和一块S区(s0或s1)的原因:
底层采用的是复制算法:当回收时,将eden和s0中还存活的对象一次性复制到另一块survivor空间s2上。最后清理掉eden和s0空间

划分新老生代的意义在于:可以根据项目中对象大小的数量,设置新生代、老生代的空间容量,从而提高GC的性能。

虚拟机参数:
-Xms128m :设置jvm启动时的大小
-Xms32m:设置新生代的大小
-Xms128m:设置JVM的总大小
JVM总大小 = 新生代 +老生代

5. 方法区(Method Area)

与Java堆一样,方法区为各个线程所共享。用于存储类的元数据(描述类的信息)、常量、方法信息(方法数据、方法代码)、即时编译器编译后的代码。方法区域与ava堆一样,不需要连续内存和可以选择固定大小或者扩展。除此之外,还可以选择不实现垃圾收集。
当方法区无法满足内存分配需求时(方法区的数据太多时),就会抛出OutOfMemoryError异常。

成员方法执行的流程:

在这里插入图片描述

6. 运行时常量池(Runtime Constant Pool)

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

Java规范对运行时常量池没有做任何细节要求,不同提供商可以按照自己的需要来实现这个内存区域。

运行时常量池还有一个 重要特征,就是具备动态性:Java语言并不要求常量一定只有编译期才能产生,也就是并非与置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。
运行时常量池是方法区的一部分,自然受方法区内存的限制,当常量池无法再申请内存时就会抛出OutOfMemoryError异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值