java的console保存不能运行_JVM(三) 运行时数据区

本文详细介绍了Java运行时数据区,包括方法区、虚拟机栈、本地方法栈、堆、程序计数器和直接内存。阐述了各区域的功能、特点、实现方式及回收机制,还说明了线程与内存的关系,以及堆、栈、方法区保存内容的区别与联系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行时数据区,用于保存Java程序运行过程中需要用到的数据和相关信息。运行时数据区包括方法区、虚拟机栈、本地方法栈、堆、程序计数器。

下图中红色区域为所有线程共享的数据区,黄色部分为线程隔离的数据区。

3a03ae7fb9af96119a015cb1311f3eef.png
内存布局

方法区:

方法区是所有线程共享的内存空间,用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是一个逻辑概念。在JDK1.8之前用PermSpace实现,JDK1.8及之后用MetaSpace实现。

永久代(PermSpace):

JDK1.8之前使用。在程序启动时会指定永久代的大小,程序运行期间也不能修改。垃圾收集模块不会清理这块内存区域,当永久代内存不够用时会抛出OOM异常。运行时常量池也存放在永久代中。

元数据区(MetaSpace):

JDK1.8之后使用。若程序启动时不指定元数据区的大小,最大可用内存就是机器的物理内存。元数据区支持动态申请内存,Full GC会回收元数据区的内存。运行时常量池存放于堆中,便于垃圾回收。

运行时常量池:

主要用于存放类,变量等的符号引用和直接引用,通过下标进行识别,符号引用解析之后的直接引用也会存放在运行时常量池中。

方法区的回收机制:

方法区回收主要是回收废弃的常量和无用的类。废弃的常量是指没有任何一个地方引用这个常量。

无用的类必须同时满足:

  1. Java堆中不存在该类的任何实例,也就是该类的所有实例都已经被回收;
  2. 加载该类的ClassLoader已经被回收;
  3. 该类对应的Class对象在任何地方没有引用了,也不能通过反射访问该类的方法。

虚拟机栈:

虚拟机栈是线程私有的内存空间,用于描述Java中方法执行的过程,包括方法调用,返回值等。

当执行或调用一个方法时,都会创建一个栈帧(Stack Frame),用于存储局部变量和部分计算结果,包括局部变量表、操作数栈、动态链接,返回值地址等。

局部变量表(Local Variable Table)

保存方法参数和内部使用到的局部变量,作用域仅在这个方法内,方法执行完成结束生命周期。

操作数栈(Operand stack)

每一个栈帧都对应着一个操作数栈,用于方法体中操作数运算时的入栈和出栈,代表的是运算的过程。

动态链接(Dynamic Linking)

指向常量池,用于标识变量,方法名,类名等的符号引用;Java中的类经过加载后会将符号引用进行解析,然后存于常量池中。

返回值地址(Return Address)

被调用方法执行结束后返回值存放的地址以及调用方法应该继续执行的指令位置;比如被调用方法return new Object(),那么会把new Object()在堆中的位置记录下来。

虚拟机栈的两种异常

如果线程请求的栈深度大于虚拟机所允许的深度,抛出 StackOverflowError 异常。
如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展),如果扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError 异常。

本地方法栈:

本地方法栈是线程私有的内存空间,和虚拟机栈类似,只不过虚拟机栈用于执行Java本身的方法,而本地方法栈用于执行本地方法、即其他语言实现的Native方法。

堆:

堆是所有线程共享的内存空间,几乎所有的对象实例以及数组的内存分配区域,垃圾收集模块的主要管理区域。

从内存回收的角度来看,由于现在垃圾收集模块基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代。再细致一点可分为:Eden空间、From Survivor空间、To Survivor空间等。

从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。

程序计数器:

程序计数器是线程私有的内存空间,可以看作是当前线程所执行的字节码的行号指示器。

直接内存:

直接内存并不是JVM运行时数据区的一部分。

在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道与缓冲区的I/O方式。它可以使用Native函数直接分配对外内存,并通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

直接内存不受Java堆大小限制,但是收到物理机总内存的限制。


关于线程和内存关系的进一步说明:

b00055c976deb0bd081f76af97e42ec4.png
线程和内存区域的关系

堆和方法区为线程共享,虚拟机栈、本地方法栈、程序计数器为线程私有。

0efa9cb749e404e7be9269001971271b.png

每个线程拥有自己独立的虚拟机栈、本地方法栈、程序计数器。

关于方法区实现和运行时常量池的进一步说明:

对于HotSpot:

56249ab5bb4c52f17a2845a78db27bf2.png

关于堆、栈、方法区中所保存的内容的区别与联系:

  1. 所有的类对象(xxx.class)、包括类的静态变量都保存在方法区中;
  2. 所有的对象实例都保存在堆中;
  3. 对于对象的引用、数组、基本数据类型,若为某个方法的局部变量,则保存在调用该方法的线程的栈中;若为某个类的静态成员变量,则保存在方法区中;若为某个类的非静态成员变量,则保存在堆中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值