JVM | 内存模型

JVM内存划分:

  1. 方法区(线程共享):存储常量、静态变量、JIT(即时编译器)编译后代码也在方法区存放
  2. 堆内存(线程共享):GC垃圾回收的主要场地
  3. 程序计算器:当前线程执行的字节码的位置指示器
  4. Java虚拟机栈(栈内存):保存局部变量,基本数据类型以及堆内存中对象的引用变量
  5. 本地方法栈(C栈):JVM提供使用native方法的服务
    在这里插入图片描述

详细介绍

1. 程序计数器

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。另外,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程间计数器互不影响,独立存储,我们称这类内存区域为线程私有的内存。

主要作用:
(1)字节码解释器通过改变线程计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择循环、异常处理
(2)在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候,能够知道该线程上次运行到的位置。

注:程序计数器是唯一一个不会出现OutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而消亡。

2. Java虚拟机栈

与程序计数器一样,Java虚拟机栈是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的结束而消亡。描述的是Java方法执行的内存模型。
Java内存可以粗糙的区分为堆内存(Heap)和栈内存(Stack),其中栈就是虚拟机栈,或者说是虚拟机栈中的局部变量表部分。
(实际上,Java虚拟机栈由一个个栈帧组成,而每个栈帧都包含:局部变量表、操作数栈、动态链接(XXX.dll)、方法出口信息)
在这里插入图片描述
java虚拟机栈会出现两种异常:StackOverFlowError(栈溢出异常)和OutOfMemoryError(OOM,内存溢出异常)

StackOverFlowError:若Java虚拟机栈的内存大小不允许动态扩展,当线程请求栈的深度超过当前Java虚拟机栈的最大深度时,就抛出StackOverFlowError异常。
OutOfMemoryError:若Java虚拟机栈的内存大小允许动态扩展,当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。

3. 本地方法栈(和虚拟机栈所发挥的作用非常相似)

区别:虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则是为虚拟机使用到的native方法服务。

本地方法被执行时,在本地方法栈会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、方法出口信息。方法执行完毕后,相应的栈帧也会出栈并释放内存空间,也会出现StackOverFlowError和OutOfMemoryError两种异常

4. Java堆

Java虚拟机所管理的内存中最大的一块,Java堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。Java堆是垃圾收集器管理的主要区域,因此也被成为GC堆(Garbage Collected Heap)。从垃圾回收的角度,由于出现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代;再细致划分有:Eden空间、From Survivor(S1)、To Survivor(S2)空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
在这里插入图片描述

永久代说明:
jdk1.6及之前:常量池分配在永久代
jdk1.7:有,但已逐步“去永久代”
jdk1.8及之后:无(java.lang.OutOfMemoryError:PermGen space,这种错误将不会出现在JDK1.8中) 在jdk1.8中移除整个永久代,取而代之的是元空间(Metaspace)区域
(永久代使用的是JVM的堆内存空间,而元空间使用的是物理内存,直接受到本机的物理内存限制)

5. 方法区

方法区与java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java对分开来。

HotSpot虚拟机中方法去也常被成为“永久代”,本质上两者并不等价,仅仅是因为HotSpot虚拟机设计团队用永久代来实现方法区而已,这样HotSpot虚拟机的垃圾收集器就可以像管理Java堆一样管理这部分内存,但是这样会造成更容易遇到内存溢出问题。

总结:

功能是否线程共享生命周期抛出异常
程序计数器当前线程执行的字节码的位置指示器线程私有随着线程的创建而创建,随着线程的结束而消亡不会出现OutOfMemoryError异常
Java虚拟机栈保存局部变量,基本数据类型以及堆内存中对象的引用变量线程私有随着线程的创建而创建,随着线程的结束而消亡StackOverFlowError、OutOfMemoryError
本地方法栈JVM提供使用native方法的服务线程私有随着线程的创建而创建,随着线程的结束而消亡StackOverFlowError、OutOfMemoryError
Java栈GC垃圾回收的主要场地线程共享与线程相同
方法区储存常量、静态变量、JIT(即时编译器) 编译后代码也在方法区存放线程共享与线程相同
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM内存模型是指Java虚拟机在运行时对内存的组织和管理方式。它定义了JVM内存的不同区域以及各个区域的作用和特点。 JVM内存模型可以分为以下几个部分: 1. 程序计数器(Program Counter Register):每个线程都有自己的程序计数器,用于记录当前线程执行的字节码指令的地址。 2. Java虚拟机栈(Java Virtual Machine Stacks):每个线程在执行Java方法时会创建一个对应的栈帧,栈帧用于存储方法的局部变量、操作数栈、方法返回值等信息。 3. 堆(Heap):堆是JVM中最大的一块内存区域,被所有线程共享。它用于存储对象实例和数组。堆内存由垃圾回收器自动管理,负责对象的分配和释放。 4. 方法区(Method Area):方法区用于存储已加载类的信息、静态变量、常量、即时编译器编译后的代码等。在JDK 8及以后的版本中,方法区被元空间(Metaspace)所取代。 5. 运行时常量池(Runtime Constant Pool):每个类或接口在编译后都会生成一个运行时常量池,用于存放编译器生成的字面量和符号引用。 6. 本地方法栈(Native Method Stacks):本地方法栈用于执行本地方法(Native Method)的栈。 7. 直接内存(Direct Memory):直接内存不是JVM管理的堆内存,而是通过操作系统本地IO直接分配的内存。一般在使用NIO(New Input/Output)时会使用到直接内存。 这些内存区域共同组成了JVM内存模型,对于Java程序的运行和性能有着重要的影响。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值