jvm的自动内存管理

jvm的自动内存管理

前言

Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域,这些区域有各自不同的用途,以及创建和销毁时间,有的区域随着虚拟机随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁,根据虚拟机规范,Java虚拟机所管理的内存包括程序技术器,虚拟机栈等几个区域。

Java虚拟机栈

Java 虚拟机栈是线程私有的,它的生命周期与线程相同,线程启动而产生,线程结束而消亡。Java 虚拟机栈是描述 Java 方法执行的内存模型,用于存储栈帧。如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常。虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError 异常。

#####栈帧

每个方法执行的同时会创建一个栈帧,它是虚拟机栈的基本元素。一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。栈帧随着方法调用而创建,随着方法结束而销毁。每一个栈帧包含的内容有局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息。

#####局部变量表

一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。该方法所需要分配的局部变量表的最大容量在将 Java 编译为 Class 文件时已经确定。一个局部变量表保存的是编译器可知的各种基本数据类型、对象引用和 returnAddress 类型(它指向了一条字节码指令的地址)。局部变量表的容量以变量槽为最小单位,每个变量槽可以存储32位长度的内存空间。对于64位长度的数据类型(long,double),虚拟机会以高位对齐方式为其分配两个连续的变量糙空间。局部变量表所需的内存空间在编译期间就能完成分配,在运行期间不会改变其大小。虚拟机通过索引定位的方法查找相应的局部变量

操作数栈

虚拟机栈中的一个用于计算的临时数据存储区。随着方法执行和字节码指令的执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。

动态链接

在一个class文件中,一个方法要调用其他方法,需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于运行时常量池。每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用。这些符号引用一部分会在类加载阶段或者第一次使用时就直接转化为直接引用,这类转化称为静态解析。另一部分将在每次运行期间转化为直接引用,这类转化称为动态连接。

方法返回

一个方法开始执行后,只有两种退出方式:正常完成出口和异常完成出口正常完成出口指方法正常完成并退出,根据当前方法返回的字节码指令,这时有可能会有返回值传递给方法调用者(调用它的方法),或者无返回值。异常完成出口指方法执行过程中遇到异常,并且这个异常在方法体内部没有得到处理,导致方法退出。无论采用何种退出方式,在方法退出后,都需要返回到方法被调用的位置,方法返回时可能需要在栈帧中保存一些信息。一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中会保存这个计数器值。而方法异常退出时,返回地址要通过异常处理器表来确定,栈帧中一般不保存这部分信息。

附加信息

虚拟机规范允许具体的虚拟机实现增加一些规范中没有描述的信息到栈帧之中,例如和调试相关的信息,这部分信息完全取决于不同的虚拟机实现。在实际开发中,一般会把动态连接,方法返回地址与其他附加信息一起归为一类,称为栈帧信息。

程序计数器

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 它是JVM 规范中唯一没有规定 OutOfMemoryError 情况的区域。

堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。

分代

JVM 中堆空间由新生代和老年代两个区组成

新生代可以划分为三个区,Eden 区,两个 Survivor 区

Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。

方法区

方法区是一种规范,不同的虚拟机的实现也不一样。从 JDK 1.8 开始,元空间(Metaspace)取代了永久代(PermGen)成为 HotSpot VM 对方法区的实现。方法区存储加载进来的每一个类的结构信息,可以看做是将类(Class)的模板信息,保存在方法区里

元空间属于本地内存

JDK8 以前,永久代是堆的一部分,和新生代、老年代的地址是连续的。JDK8 以后,元空间属于本地内存,不再属于堆的一部分,它还有一个别名叫非堆(Non-Heap),所以元空间不存在 OOM 内存溢出的情况。

方法区是线程共享的

当多个线程用到同一个类,而这个类还未被加载,则应该只有一个线程去加载类,其他线程等待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值