Java虚拟机的内存管理

Java虚拟机的内存管理

一、JVM整体机构

​ JVM 是 Java Virtual Machine(Java虚拟机)的缩写,JVM 是一种用于计算设备的规范,它是一个虚构出来的计算 机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

​ 根据 JVM 规范,JVM 内存共分为虚拟机栈、本地方法栈、程序计数器、方法区、堆五大部分。如下图所示:

在这里插入图片描述

​ JVM分为五大模块: 类装载器子系统 、 运行时数据区 、 执行引擎 、 本地方法接口和垃圾收集模块 ,如下图所示:

在这里插入图片描述

二、运行时内存

​ JDK1.8 的内存结构与 JDK1.7 有所不同,在 Java8 中,元空间 (Metaspace) 登上舞台,方法区存在于元空间 (Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)。具体如下图所示:

在这里插入图片描述

1. PC程序计数器

​ 程序计数器(Program Counter Register):也叫PC寄存器,是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

​ Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。 因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

2. Java虚拟机栈

​ Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,即生命周期和线程相同。Java虚拟机栈和线程同时创建,用于存储栈帧。每个方法在执行时都会创建一个栈帧(Stack Frame),栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机栈里从入栈到出栈的过程。

在这里插入图片描述

3. 本地方法栈

​ 本地方法栈(Native Method Stacks) 与虚拟机栈所发挥的作用是非常相似的, 其区别只是虚拟机栈为虚拟机执行 Java方法(也就是字节码) 服务, 而本地方法栈则是为虚拟机使用到的本地(Native) 方法服务。本地方法栈是线程私有的,它的生命周期与线程相同,每个线程都有一个。

4. 堆
4.1 Java堆的概念

​ 对于Java应用程序来说, Java 堆(Java Heap) 是虚拟机所管理的内存中最大的一块。 Java堆是被所有线程共享的一块内存区域, 在虚拟机启动时创建。 此内存区域的唯一目的就是存放对象实例, Java 世界里“几乎”所有的对象实例都在这里分配内存。

​ Java 堆是垃圾收集器管理的主要区域。 因此很多时候 Java 堆也被称为“GC堆”(Garbage Collected Heap)。Java 堆是计算机物理存储上不连续的、逻辑上是连续的,也是大小可调节的(通过-Xms和-Xmx控制)。 方法结束后,堆中对象不会马上移出仅仅在垃圾回收的时候时候才移除。

4.2 堆空间的分类

​ 在 Java8 以后,Hotspot 虚拟机将 Java 堆空间分为了两部分:青年代(Young Generation)和 老年代(Old Generation)。

​ (1)青年代(Young Gen):青年代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个伊甸园区(Eden Space)和2个幸存者区(Suvivor Space)(from 和to)。

​ (2)老年代(Old Gen):老年代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍 然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。

在这里插入图片描述

4.3 对象的分配过程

(1)new的对象先放在伊甸园区。该区域有大小限制 。

(2)当伊甸园区域填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园预期进行垃圾回收(Minor GC),将伊甸园区域中不再被其他对象引用的对象进行销毁,再加载新的对象放到伊甸园区。

(3)然后将伊甸园区中的没有被回收对象移动到幸存者0区 。

(4)如果再次触发垃圾回收,此时上次幸存下来的放在幸存者0区的,如果没有回收,就会放到幸存者1区 。

(5)如果再次经历垃圾回收,此时会重新返回幸存者0区,接着再去幸存者1区。

(6)如果累计次数到达默认的15次,这会进入养老区。 阈值可以通过设置参数调整 。

(7)养老区内存不足是,会再次出发GC(Major GC) 进行养老区的内存清理 。

(8)如果养老区执行了Major GC后仍然没有办法进行对象的保存,就会报OOM异常。

5. 方法区

​ 方法区(Method Area) 与 Java 堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载的类型信息、 常量、 静态变量、 即时编译器编译后的代码缓存等数据。

​ 元空间、永久代是方法区具体的落地实现。方法区看作是一块独立于Java堆的内存空间,它实例的物理内存空间不连续且可以选择固定大小或者动态变化,它主要是用来存储所加载的类信息的。

​ 类加载器将Class文件加载到内存之后,将类的信息存储到方法区中。

​ 方法区中存储的内容: 类型信息(域信息、方法信息) 运行时常量池

6. 元空间

​ 在 JDK1.7 之前,HotSpot 虚拟机把方法区当成永久代(PermGen)来进行垃圾回收。而从 JDK 1.8 开始,移除永久代,并把方法区移至元空间(Metaspace),它位于本地内存中,而不是虚拟机内存中。

6.1 元空间和永久代的不同
  • 存储位置不同:永久代在物理上是堆的一部分,和新生代、老年代的地址是连续的,而元空间属于本地内存。
  • 存储内容不同:在原来的永久代划分中,永久代用来存放类的元数据信息、静态变量以及常量池等。现在类的元信息存储在元空间中,静态变量和常量池等并入堆中,相当于原来的永久代中的数据,被元空间和堆内存给瓜分了。
6.2 引入元空间的原因
  • 字符串存在永久代中,容易出现性能问题和内存溢出。
  • 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  • Oracle 可能会将HotSpot 与 JRockit 合二为一,JRockit没有所谓的永久代。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我真真的是小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值