JVM笔记2 JVM内存结构 即 java运行时数据区

JVM内存结构的概念模型

根据《Java虚拟机规范》 的规定, Java虚拟机所管理的内存将会包括以下几个运行时数据区域。它代表了所有虚拟机的统一外观, 但各款具体的Java虚拟机并不一定要完全照着概念模型的定义来进行设计, 可能会通过一些更高效率的等价方式去实现它。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PC程序计数器

  • 程序计数器(Program Counter Register) 是一块较小的内存空间, 它可以看作是当前线程所执行的字节码的行号指示器。
  • 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令, 它是程序控制流的指示器, 分支、 循环、 跳转、 异常处理、 线程恢复等基础功能都需要依赖这个计数器来完成。
  • 与CPU的pc寄存器对比。
    • 都是存放 “伪指令”
    • jvm的pc寄存器是内存,当虚拟机执行本地(native)方法时,jvm的pc寄存器是undefined。
  • 程序计数器是线程私有的,它的生命周期与线程相同,每个线程都有一个。
  • 此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

虚拟机栈

  • Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,即生命周期和线程相同。
  • Java虚拟机栈和线程同时创建,用于存储栈帧。
  • 每个方法在执行时都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 每一个方法从调用直到执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  • 经常有人把Java内存区域笼统地划分为堆内存(Heap) 和栈内存(Stack),这种划分方式直接继承自传统的C、 C++程序的内存布局结构。 这种说法下的“栈”通常就是指虚拟机栈中局部变量表。
    在这里插入图片描述

局部变量表

局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。包括8种基本数据类型、对象引用(reference类型)和returnAddress类型(指向一条字节码指令的地址)。
其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。

操作数栈

操作数栈(Operand Stack)也称作操作栈,是一个后入先出栈(LIFO)。随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。

动态链接

Java虚拟机栈中,每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,持有这个引用的目的是为了支持方法调用过程中的动态链接(Dynamic Linking)。
动态链接的作用:将符号引用转换成直接引用。

方法返回地址

方法返回地址存放调用该方法的PC寄存器的值。
一个方法的结束,有两种方式:正常地执行完成,出现未处理的异常非正常的退出。
无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。
方法正常退出时,调用者的PC计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。
而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
无论方法是否正常完成,都需要返回到方法被调用的位置,程序才能继续进行。

本地方法栈

本地方法栈(Native Method Stacks) 与虚拟机栈所发挥的作用是非常相似的, 其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码) 服务, 而本地方法栈则是为虚拟机使用到的本地(Native) 方法服务。
native类方法填补java代码不方便实现的部分。
Hot-Spot虚拟机的实现就是 直接就把本地方法栈和虚拟机栈合二为一。

方法区:Method Area

  • 元空间Metaspace、永久代PermanentGeneration是方法区实现
  • 方法区包括:
    • 类信息:对应class文件中的信息,主要包括类型信息,字段信息,方法信息,常量池等
    • 运行时常量池:class文件中的常量池的运行时,主要是将部分符号引用转化为了直接引用
    • oop即class实例:
    • 字符串产量池:本质上是个hashMap,存放字符串常量或者字符串对象的引用(JDK7.0,String#intern())
      在这里插入图片描述

为什么移除永久代

  • 字符串常量池存在常量池中,职责不清,改进:字符串常量池放到堆中
  • 永生代回收条件苛刻,回收效率低,改进:元空间放到本地内存,fullGC回收
  • 永生代大小不好设置,改进:元空间最大内存无限

字符串常量池(String Constant Pool)

字符串常量池在Java内存区域的哪个位置?

在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中;
在JDK7.0版本,字符串常量池被移到了堆中了。至于为什么移到堆内,大概是由于方法区的内存空间太小了。

字符串常量池是什么?

在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个Hash表,默认值大小长度是1009;
这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。
字符串常量由一个一个字符组成,放在了StringTable上。
在JDK6.0中,StringTable的长度是固定的,长度就是1009,因此如果放入String Pool中的String非常多,就会造成hash冲突,导致链表过长,当调用String#intern()时会需要到链表上一个一个找,从而导致性能大幅度下降;
在JDK7.0中,StringTable的长度可以通过参数指定:-XX:StringTableSize=66666

字符串常量池里放的是什么?

在JDK6.0及之前版本中,String Pool里放的都是字符串常量;
在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用。

Java堆

  • 所有线程共享
  • 虚拟机启动时创建
  • 存放:对象实例 + 静态变量 + 字符串常量池 + 线程分配缓冲区
  • 不是所有对象实例都在堆中,比如栈上分配、 标量替换的优化技术导致对象在栈上创建
  • Java堆是计算机物理存储上不连续的、逻辑上是连续的,也是大小可调节的(通过-Xms和-Xmx控制)。
  • Java堆是垃圾收集器管理的主要区域。
  • Java堆中经常会出现“新生代”“老年代”“永久代”“Eden空间”“From Survivor空间”“To Survivor空间”等名词不是Java虚拟机规范里对Java堆的进一步细致划分。这些区域划分仅仅是一部分垃圾收集器的设计风格, 而非某个Java虚拟机具体实现的固有内存布局。

直接内存

直接内存(Direct Memory) 并不是虚拟机运行时数据区的一部分。
在JDK 1.4中新加入了NIO(New Input/Output) 类, 引入了一种基于通道(Channel) 与缓冲区 (Buwer) 的I/O方式, 它可以使用Native函数库直接分配堆外内存, 然后通过一个存储在Java堆里面的 DirectByteBuwer对象作为这块内存的引用进行操作。 这样能在一些场景中显著提高性能, 因为避免了 在Java堆和Native堆中来回复制数据。
在这里插入图片描述
NIO的Buwer提供一个可以直接访问系统物理内存的类——DirectBuwer。DirectBuwer类继承自ByteBuwer,但和普通的ByteBuwer不同。普通的ByteBuwer仍在JVM堆上分配内存,其最大内存受到最大堆内存的 限制。而DirectBuwer直接分配在物理内存中,并不占用堆空间。在访问普通的ByteBuwer时,系统总是会使用一个“内核缓冲区”进行操作。而DirectBuwer所处的位置,就相当于这个“内核缓冲区”。因此,使用DirectBuwer是一种更加接近内存底层的方法,所以它的速度比普通的ByteBuwer更快。
通过使用堆外内存,可以带来以下好处:

  1. 改善堆过大时垃圾回收效率,减少停顿。Full GC时会扫描堆内存,回收效率和堆大小成正比。Native的内存,由OS负责管理和回收。
  2. 减少内存在Native堆和JVM堆拷贝过程,避免拷贝损耗,降低内存使用。
  3. 可突破JVM内存大小限制。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值