【Java进阶】大白话详解Java内存模型,纯手撸


下面的内容如果我没有专门提及Java版本,则都是以Java8+为例,已经过期的Java7-就不过多研究了。

内容仅为个人观点,如果有不对的请留言互相学习。

1. Java内存模型

在Java8+中内存模型中主要包括:

① 堆内存(年轻代、老年代、字符串常量池)

② 其他的非堆内存,包括了元空间(类的元数据、运行时常量池)、代码缓存、线程栈

img

1.1. 字符串常量池和运行时常量池

这两个都是常量池,为什么一个在堆内存中,另一个在元空间中呢?

运行时常量池中存储的是字符串常量池的引用,可以理解为你把文件存到了D盘,但给这个文件创建了一个快捷方式,把快捷方式存在C盘,这样你就可以直接在C盘快速的访问D盘中的文件了。

基本类型常量(如intfloat)会嵌入代码或存于运行时常量池。

假设我们有以下代码:

String s = "hello";

它的加载方式如下图所示:

img

在编译阶段时,会先根据.class文件生成符号引用的记录。

在类加载阶段,把这个生成的符号引用记录加载到运行时常量池中,此时还没有创建实际的字符串。

在运行阶段,JVM才会根据符号引用记录去字符串常量池中创建,就相当于是运行之前,JVM假装已经有了这个字符串(反正也暂时不用),运行的时候才去真的创建。

**注意:**这里有一个误区,String s = "hello"String s = new String("hello")加载方式不一样。后者因为使用了new,所以在堆中创建了两个对象。

1.2. 年轻代和老年代

年轻代和老年代是堆内存的主要区域,每一个新建对象都会先进入年轻代中的Eden 区,当Eden 区满时会触发Minor GC,GC过后Eden 区幸存下来以及非空闲幸存者区的对象会通过复制算法复制到空闲的幸存者区(S0和S1始终会保持其中一个为空),复制完成后Eden区和原非空闲幸存者区对象会被清空。

年轻代内存分布图

每一次 Minor GC年轻代中存活的对象年龄都会+1,当幸存者区中的对象年龄≥15时会晋升至老年代中。

在Java8中会动态调整晋升阈值,如果某年龄对象总大小 > Survivor 区 50%,则≥该年龄的对象直接晋升。

对象在堆内存中完整生命周期

这个是理想情况下是这样的,但有时候就是不太理想,比如创建的对象太大幸存者区放不下的情况都会导致对象直接被放到了老年代中。

而晋升到老年代的对象太多时,就会导致频繁的Full GC,,导致性能下降甚至OOM,如果确实业务需要,可以考虑增加堆空间大小(-Xmx)或者调整老年代的比例(默认老年代和年轻代是2/1)。

1.3. 永久代和元空间

Java8之前的版本中还有一个叫永久代的东西,在Java8中大部分作用被元空间取代了,但也并不是替换这么简单,Java8之前的永久代跟年轻代、老年代一样是在堆内存中,现在的元空间并没有在堆内存中。

由于永久代是堆内存的一部分,所以它的内存空间是固定的。而元空间并不受堆内存的影响,它是动态扩展,你电脑系统的内存有多大它就能用多大,当然你也可以通过配置JVM参数-XX:MaxMetaspaceSize设置上限。

上文说到的字符串常量池在Java8之前是在永久代中的,Java8之后字符串常量池的“老大”永久代被干掉了,字符串常量池翻身当大哥了,直接属于堆内存中的一块空间了。

1.4. GC行为

上文中多次出现Minor GC和Full GC并不是一种具体的垃圾回收算法,而是指一种行为,Minor GC是指局部回收,一般用在年轻代中的回收,所以也被称 Young GC。而Full GC是指全局回收,整个堆内存和元空间都会被回收。

在GC过程中会STW(Stop-The-World),就是说这个期间会把正在运行的线程暂停,是为了对象引用不会在这期间修改,可以类比一个清洁工来办公室清理垃圾,但得让你们腾地方,就会导致此期间无法工作。一般Minor GC行为的STW时间是毫秒级,但Full GC是全堆清理,它的STW时间一般都得秒级,甚至能达到30s以上。

除了上述两种GC行为,还有另一种Java9+才应用的Mixed GC行为,即混合行为。就是为了防止上述说的Full GC时秒级的STW。Mixed GC能让垃圾回收像回收年轻代一样回收老年代,不需要全堆的停顿,一般是将回收过程分多次进行,这样也可以分摊停顿时间。

但被用的最多的Java8当老年代满时,就会触发Full GC,不会进行Mixed GC,要在Java 8中使用Mixed GC需要手动配置,并且不太稳定,建议是直接升级JDK版本到JDK11+。

如果需要在Java8中使用Mixed GC行为,可以通过配置如下参数:

-XX:+UseG1GC # 使用G1垃圾回收器
-XX:InitiatingHeapOccupancyPercent=45 # 触发并发标记的老年代阈值(老年代占堆内存比例)
-XX:G1MixedGCLiveThresholdPercent=85 # Mixed GC回收区域的存活率上限(存活对象 > 85% 的区域不回收)
-XX:G1HeapWastePercent=10 # 碎片空间占堆内存比例,也就是允许10%空间浪费

调优指南

① -XX:G1MixedGCLiveThresholdPercent=85可能会因为太多区域被跳过,导致每次GC堆释放空间太少,从而导致频繁的GC。需要根据自己系统业务,判断是否会存在这个情况来调整比例。

② -XX:G1HeapWastePercent=10可能会导致堆中有大量碎片空间,但还没有达到10%的这个阈值,可以根据自己系统堆空间大小来实际调整。

1.5. GC类型

六大GC类型垃圾收集器对比表

收集器年轻代算法老年代算法优势适用场景
Serial GC复制标记-整理单线程低开销客户端/嵌入式(资源受限)
Parallel GC复制(多线程)标记-整理(多线程)高吞吐量计算密集型后端
CMS复制并发标记-清除低停顿(老年代)响应敏感的 Web 服务(已废弃)
G1 GC复制(Region)复制+标记-整理平衡吞吐与停顿大堆内存(6GB~16GB)
ZGC并发复制并发复制亚毫秒停顿超大堆(TB级)、低延迟要求
Shenandoah GC并发复制并发复制低停顿+高吞吐大堆内存、混合负载

JDK 8 默认:Parallel GC(吞吐优先)

JDK 9~14 默认:G1 GC(平衡型)

JDK 15+ :ZGC/Shenandoah(极致低延迟)

下面是主要的几个GC收集器的STW停顿时间

主要GC收集器的STW停顿时间

### JVM内存模型的通俗解释 JVM内存模型Java虚拟机在运行时如何分配和管理内存的规范[^1]。它描述了Java程序运行时,内存是如何被划分为不同的区域以及这些区域的作用。 #### 1. JVM内存模型的主要组成部分 JVM内存模型可以分为以下几个主要部分: - **方法区(Method Area)** 方法区用于存储已被虚拟机加载的类信息、常量、静态变量以及编译后的代码等数据。在某些JVM实现中,方法区被称为永久代(Permanent Generation),但由于其空间有限,当加载的类过多时,可能会导致`OutOfMemoryError:PermGen space`异常[^3]。 - **堆(Heap)** 堆是JVM中最大的一块内存区域,用于存放对象实例和数组。堆的大小可以通过`-Xms`(最小值)和`-Xmx`(最大值)参数进行设置。默认情况下,当空余堆内存小于40%时,JVM会增大堆内存到`-Xmx`指定的大小[^4]。 - **栈(Stack)** 每个线程都有自己独立的栈,栈中保存的是局部变量表、操作数栈、动态链接、方法出口等信息。每个方法执行时都会创建一个栈帧(Frame),并将其压入栈中,方法执行完毕后,栈帧会被弹出。 - **本地方法栈(Native Method Stack)** 与普通栈类似,但本地方法栈为虚拟机使用的Native方法服务。 - **程序计数器(Program Counter Register)** 程序计数器是一个较小的内存区域,用于记录当前线程所执行的字节码指令的位置。如果是执行Java方法,则记录的是JVM字节码指令地址;如果是执行本地方法,则为空。 #### 2. JVM内存模型的工作原理 JVM通过将Java代码编译成字节码,并在运行时解释或编译成机器码来执行。对于“热点”代码,JVM会采用即时编译(JIT)技术,将字节码编译为本地机器码并缓存起来,从而提高执行效率[^5]。 #### 3. 内存分配与垃圾回收 JVM中的堆内存是垃圾回收的主要区域。对象的分配通常发生在堆上,而垃圾回收机制则负责回收不再使用的对象,释放内存空间。如果堆内存不足且无法扩展,JVM会抛出`OutOfMemoryError`异常。 ```python # 示例:触发OutOfMemoryError try: while True: list = [] for i in range(1000000): list.append("Test") # 不断分配内存 except MemoryError as e: print(f"内存不足: {e}") ``` #### 4. 平台无关性 JVM屏蔽了底层操作系统的差异,使得Java程序只需生成针对JVM的字节码,就可以在不同平台上运行,实现了“一次编译,到处运行”的目标[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

二饭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值