深入理解Java虚拟机JVM内存模型与垃圾回收机制详解

深入理解Java虚拟机(JVM)内存模型

Java虚拟机(JVM)是Java语言实现跨平台能力的核心,其内存模型与垃圾回收机制是保证Java应用高效、稳定运行的关键。JVM内存区域主要划分为线程私有的程序计数器、Java虚拟机栈、本地方法栈,以及所有线程共享的堆、方法区(或元空间)和运行时常量池。程序计数器是当前线程所执行的字节码的行号指示器,控制着分支、循环、跳转、异常处理等基本流程。Java虚拟机栈用于存储栈帧,每个方法从调用到执行完成的过程对应一个栈帧的入栈和出栈,栈帧中包含了局部变量表、操作数栈、动态链接和方法返回地址等信息。

堆内存的详细划分

堆是JVM管理中最大、最重要的一块内存区域,几乎所有通过`new`关键字创建的对象实例和数组都在这里分配内存。为了更高效地进行内存回收,现代垃圾回收器通常将堆进一步细分。年轻代(Young Generation)是对象诞生和成长的初始区域,绝大多数新创建的对象首先被分配在这里。年轻代又可以分为一个Eden(伊甸园)区和两个Survivor(幸存者)区(通常称为S0和S1或From和To)。新对象在Eden区被创建,当Eden区满时,会触发一次Minor GC(年轻代垃圾回收)。在这次GC中,存活的对象会被移动到其中一个Survivor区。随后,在每次Minor GC时,Eden区和正在使用的Survivor区中存活的对象会被复制到另一个空闲的Survivor区。经过多次Minor GC(默认15次)仍然存活的对象,会被晋升(Promote)到老年代(Old Generation)。老年代用于存放生命周期较长的对象,当老年代空间不足时,会触发Major GC或Full GC,其回收速度通常比Minor GC慢得多。

方法区与元空间

方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。在JDK 8之前,方法区的实现通常被称为“永久代”(PermGen)。但从JDK 8开始,永久代被移除,取而代之的是元空间(Metaspace)。元空间不再使用JVM的堆内存,而是使用本地内存(Native Memory)。这一改变使得元空间的大小理论上只受本地内存大小的限制,有效避免了永久代常见的`java.lang.OutOfMemoryError: PermGen space`错误。运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。

JVM垃圾回收(GC)机制详解

垃圾回收是JVM自动管理内存的核心机制,其任务是识别并回收不再被使用的对象所占用的内存,防止内存泄漏。垃圾回收器判断对象是否存活的算法主要有两种:引用计数法和可达性分析算法。引用计数法简单但难以解决对象间循环引用的问题,因此主流JVM均采用可达性分析算法。该算法通过一系列称为“GC Roots”的根对象作为起始点,从这些节点开始向下搜索,所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,可以被回收。

常见的垃圾回收算法

标记-清除(Mark-Sweep)算法是最基础的收集算法,分为“标记”和“清除”两个阶段。首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。它的主要不足是效率问题和空间问题(会产生大量不连续的内存碎片)。复制(Copying)算法将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这种算法实现简单,运行高效,但代价是内存缩小为了原来的一半,且当对象存活率高时复制操作会很多。标记-整理(Mark-Compact)算法标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。分代收集(Generational Collection)算法是当前商业虚拟机普遍采用的算法,它根据对象存活周期的不同将内存划分为几块,然后根据各个年代的特点采用最适当的收集算法。一般是将Java堆分为年轻代和老年代,在年轻代中,有大量对象“朝生夕死”,故采用复制算法;而老年代中对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记-清除”或“标记-整理”算法来进行回收。

经典的垃圾回收器

JVM提供了多种垃圾回收器供用户根据不同应用场景进行选择。Serial收集器是一个单线程的收集器,在进行垃圾收集时,必须暂停所有其他工作线程(“Stop The World”),它简单而高效,是Client模式下虚拟机默认的新生代收集器。ParNew收集器本质上是Serial收集器的多线程并行版本,是许多运行在Server模式下的虚拟机中首选的新生代收集器。Parallel Scavenge收集器也是一个用于新生代的多线程收集器,它的目标是达到一个可控制的吞吐量(吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间))。Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用“标记-整理”算法。Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法,注重吞吐量。CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它允许垃圾收集线程与用户线程(基本上)同时工作,其收集过程包括初始标记、并发标记、重新标记和并发清除四个步骤。G1(Garbage-First)收集器是面向服务端应用的垃圾收集器,它将整个Java堆划分为多个大小相等的独立区域(Region),并跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage-First名称的来由),从而建立起“停顿时间模型”。而在JDK 11中引入的ZGC和Shenandoah等收集器,则更进一步地致力于实现低延迟(将停顿时间控制在10ms以内)的大内存垃圾回收。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值