面渣逆袭 JVM篇暗黑V2.1




⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
No. 1 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
前⾔
2.3 万字 115 张⼿绘图,详解 54 道 Java 虚拟机⾯试⾼频题(让天下没有难背的⼋股),⾯
渣背会这些 JVM ⼋股⽂,这次吊打⾯试官,我觉得稳了(⼿动 dog)。整理:沉默王⼆,戳
转载链接,作者:三分恶,戳原⽂链接。
亮⽩版本更适合拿出来打印,这也是很多学⽣党喜欢的⽅式,打印出来背诵的效率会更⾼。
2024 年 12 ⽉ 30 ⽇开始着⼿第⼆版更新。
对于⾼频题,会标注在《Java ⾯试指南(付费)》中出现的位置,哪家公司,原题是什
么,并且会加 ,⽬录⼀⽬了然;如果你想节省时间的话,可以优先背诵这些题⽬,尽
快做到知彼知⼰,百战不殆。
区分⼋股精华回答版本和原理底层解释,让⼤家知其然知其所以然,同时⼜能做到⾯试时
的⾼效回答。
结合项⽬(技术派、pmhub)来组织语⾔,让⾯试官最⼤程度感受到你的诚意,⽽不是
机械化的背诵。
No. 2 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
修复第⼀版中出现的问题,包括球友们的私信反馈,⽹站留⾔区的评论,以及 GitHub 仓
库中的 issue,让这份⾯试指南更加完善。
增加⼆哥编程星球的球友们拿到的⼀些 offer,对⾯渣逆袭的感谢,以及对简历修改的⼀
些认可,以此来激励⼤家,给⼤家更多信⼼。
优化排版,增加⼿绘图,重新组织答案,使其更加⼝语化,从⽽更贴近⾯试官的预期。
⻓按识别下⾯的⼆维码,关注⼆哥的公众号,回复【222】即可拉取最新版本。
No. 3 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
当然了,请允许我的⼀点点私⼼,那就是星球的 PDF 版本会⽐公众号早⼀个⽉时间,毕竟星
球⽤户都付费过了,我有必要让他们先享受到⼀点点福利。相信⼤家也都能理解,毕竟在线
版是免费的,CDN、服务器、域名、OSS 等等都是需要成本的。
更别说我付出的时间和精⼒了,⼤家觉得有帮助还请给个⼝碑,让你身边的同事、同学都能
受益到。
No. 4 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
我把⼆哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有⾯渣逆袭的版本都
放进来了,涵盖 Java基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机⽹络、
操作系统、MySQL、Redis、RocketMQ、分布式、微服务、设计模式、Linux 等 16 个⼤的
主题,共有 40 多万字,2000+张⼿绘图,可以说是诚意满满。
展示⼀下暗⿊版本的 PDF 吧,排版清晰,字体优雅,更加适合夜服,晚上看会更舒服⼀点。
No. 5 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
⼀、引⾔
1.什么是 JVM?
JVM,也就是 Java 虚拟机,它是 Java 实现跨平台的基⽯。
程序运⾏之前,需要先通过编译器将 Java 源代码⽂件编译成 Java 字节码⽂件;
程序运⾏时,JVM 会对字节码⽂件进⾏逐⾏解释,翻译成机器码指令,并交给对应的操作系
统去执⾏。
No. 6 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
这样就实现了 Java ⼀次编译,处处运⾏的特性。
说说 JVM 的其他特性?
①、JVM 可以⾃动管理内存,通过垃圾回收器回收不再使⽤的对象并释放内存空间。
②、JVM 包含⼀个即时编译器 JIT,它可以在运⾏时将热点代码缓存到 codeCache 中,下次
执⾏的时候不⽤再⼀⾏⼀⾏的解释,⽽是直接执⾏缓存后的机器码,执⾏效率会⼤幅提⾼。
No. 7 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
③、任何可以通过 Java 编译的语⾔,⽐如说 Groovy、Kotlin、Scala 等,都可以在 JVM 上
运⾏。
No. 8 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
为什么要学习 JVM?
学习 JVM 可以帮助我们开发者更好地优化程序性能、避免内存问题。
⽐如说了解 JVM 的内存模型和垃圾回收机制,可以帮助我们更合理地配置内存、减少 GC 停
顿。
⽐如说掌握 JVM 的类加载机制可以帮助我们排查类加载冲突或异常。
再⽐如说,JVM 还提供了很多调试和监控⼯具,可以帮助我们分析内存和线程的使⽤情况,
从⽽解决内存溢出内存泄露等问题。
1. Java ⾯试指南(付费)收录的京东同学 10 后端实习⼀⾯的原题:有了解 JVM
吗
2. Java ⾯试指南(付费)收录的字节跳动同学 20 测开⼀⾯的原题:了解过 JVM
么?讲⼀下 JVM 的特性
2.说说 JVM 的组织架构(补充)
No. 9 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
增补于 2024 年 03 ⽉ 08 ⽇。
推荐阅读:⼤⽩话带你认识 JVM
JVM ⼤致可以划分为三个部分:类加载器、运⾏时数据区和执⾏引擎。
① 类加载器,负责从⽂件系统、⽹络或其他来源加载 Class ⽂件,将 Class ⽂件中的⼆进制
数据读⼊到内存当中。
② 运⾏时数据区,JVM 在执⾏ Java 程序时,需要在内存中分配空间来处理各种数据,这些
内存区域按照 Java 虚拟机规范可以划分为⽅法区、堆、虚拟机栈、程序计数器和本地⽅法
栈。
③ 执⾏引擎,也是 JVM 的⼼脏,负责执⾏字节码。它包括⼀个虚拟处理器、即时编译器 JIT
和垃圾回收器。
No. 10 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
1. Java ⾯试指南(付费)收录的腾讯 Java 后端实习⼀⾯原题:说说 JVM 的组织
架构
2. Java ⾯试指南(付费)收录的得物⾯经同学 9 ⾯试题⽬原题:JVM的架构,具
体阐述⼀下各个部分的功能?
⼆、内存管理
3. 能说⼀下 JVM 的内存区域吗?
推荐阅读:深⼊理解 JVM 的运⾏时数据区
按照 Java 虚拟机规范,JVM 的内存区域可以细分为 程序计数器 、 虚拟机栈 、 本地⽅法栈 、
堆 和 ⽅法区 。
No. 11 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
其中 ⽅法区 和 堆 是线程共享的, 虚拟机栈 、 本地⽅法栈 和 程序计数器 是线程私有的。
介绍⼀下程序计数器?
程序计数器也被称为 PC 寄存器,是⼀块较⼩的内存空间。它可以看作是当前线程所执⾏的字
节码⾏号指示器。
介绍⼀下 Java 虚拟机栈?
Java 虚拟机栈的⽣命周期与线程相同。
No. 12 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
当线程执⾏⼀个⽅法时,会创建⼀个对应的栈帧,⽤于存储局部变量表、操作数栈、动态链
接、⽅法出⼝等信息,然后栈帧会被压⼊虚拟机栈中。当⽅法执⾏完毕后,栈帧会从虚拟机
栈中移除。
⼀个什么都没有的空⽅法,空的参数都没有,那局部变量表⾥有没有变量?
对于静态⽅法,由于不需要访问实例对象 this,因此在局部变量表中不会有任何变量。
对于⾮静态⽅法,即使是⼀个完全空的⽅法,局部变量表中也会有⼀个⽤于存储 this 引⽤的
变量。this 引⽤指向当前实例对象,在⽅法调⽤时被隐式传⼊。
No. 13 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
详细解释⼀下:
⽐如说有这样⼀段代码:
public class VarDemo1 {
public void emptyMethod() {
// 什么都没有
}
public static void staticEmptyMethod() {
// 什么都没有
}
}
⽤ javap -v VarDemo1 命令查看编译后的字节码,就可以在 emptyMethod 中看到这样的内
容:
这⾥的 locals=1 表示局部变量表有⼀个变量,即 this,Slot 0 位置存储了 this 引⽤。
⽽在静态⽅法 staticEmptyMethod 中,你会看到这样的内容:
No. 14 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
这⾥的 locals=0 表示局部变量表为空,因为静态⽅法属于类级别⽅法,不需要 this 引⽤,也
就没有局部变量。
介绍⼀下本地⽅法栈?
本地⽅法栈与虚拟机栈相似,区别在于虚拟机栈是为 JVM 执⾏ Java 编写的⽅法服务的,⽽
本地⽅法栈是为 Java 调⽤本地 native ⽅法服务的,通常由 C/C++ 编写。
在本地⽅法栈中,主要存放了 native ⽅法的局部变量、动态链接和⽅法出⼝等信息。当⼀个
Java 程序调⽤⼀个 native ⽅法时,JVM 会切换到本地⽅法栈来执⾏这个⽅法。
介绍⼀下本地⽅法栈的运⾏场景?
当 Java 应⽤需要与操作系统底层或硬件交互时,通常会⽤到本地⽅法栈。
⽐如调⽤操作系统的特定功能,如内存管理、⽂件操作、系统时间、系统调⽤等。
详细说明⼀下:
⽐如说获取系统时间的 System.currentTimeMillis() ⽅法就是调⽤本地⽅法,来获取操作
系统当前时间的。
No. 15 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
再⽐如 JVM ⾃身的⼀些底层功能也需要通过本地⽅法来实现。像 Object 类中的
hashCode() ⽅法、 clone() ⽅法等。
native ⽅法解释⼀下?
推荐阅读:⼿把⼿教你⽤ C语⾔实现 Java native 本地⽅法
native ⽅法是在 Java 中通过 native 关键字声明的,⽤于调⽤⾮ Java 语⾔,如 C/C++ 编写
的代码。Java 可以通过 JNI,也就是 Java Native Interface 与底层系统、硬件设备、或者本
地库进⾏交互。
介绍⼀下 Java 堆?
No. 16 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
堆是 JVM 中最⼤的⼀块内存区域,被所有线程共享,在 JVM 启动时创建,主要⽤来存储
new 出来的对象。
Java 中“⼏乎”所有的对象都会在堆中分配,堆也是垃圾收集器管理的⽬标区域。
从内存回收的⻆度来看,由于垃圾收集器⼤部分都是基于分代收集理论设计的,所以堆⼜被
细分为 新⽣代 、 ⽼年代 、 Eden空间 、 From Survivor空间 、 To Survivor空间 等。
No. 17 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
随着 JIT 编译器的发展和逃逸技术的逐渐成熟,“所有的对象都会分配到堆上”就不再那么绝对
了。
从 JDK 7 开始,JVM 默认开启了逃逸分析,意味着如果某些⽅法中的对象引⽤没有被返回或
者没有在⽅法体外使⽤,也就是未逃逸出去,那么对象可以直接在栈上分配内存。
堆和栈的区别是什么?
堆属于线程共享的内存区域,⼏乎所有 new 出来的对象都会堆上分配,⽣命周期不由单个⽅
法调⽤所决定,可以在⽅法调⽤结束后继续存在,直到不再被任何变量引⽤,最后被垃圾收
集器回收。
栈属于线程私有的内存区域,主要存储局部变量、⽅法参数、对象引⽤等,通常随着⽅法调
⽤的结束⽽⾃动释放,不需要垃圾收集器处理。
介绍⼀下⽅法区?
⽅法区并不真实存在,属于 Java 虚拟机规范中的⼀个逻辑概念,⽤于存储已被 JVM 加载的
类信息、常量、静态变量、即时编译器编译后的代码缓存等。
在 HotSpot 虚拟机中,⽅法区的实现称为永久代 PermGen,但在 Java 8 及之后的版本中,
已经被元空间 Metaspace 所替代。
No. 18 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
变量存在堆栈的什么位置?
对于局部变量,它存储在当前⽅法栈帧中的局部变量表中。当⽅法执⾏完毕,栈帧被回收,
局部变量也会被释放。
public void method() {
int localVar = 100; // 局部变量,存储在栈帧中的局部变量表⾥
}
对于静态变量来说,它存储在 Java 虚拟机规范中的⽅法区中,在 Java 7 中是永久带,在
Java8 及以后 是元空间。
public class StaticVarDemo {
public static int staticVar = 100; // 静态变量,存储在⽅法区中
}
1. Java ⾯试指南(付费)收录的京东同学 10 后端实习⼀⾯的原题:堆和栈的区别
是什么
2. Java ⾯试指南(付费)收录的⽐亚迪⾯经同学 3 Java 技术⼀⾯⾯试原题:介绍
⼀下 JVM 运⾏时数据区
3. Java ⾯试指南(付费)收录的字节跳动⾯经同学 1 Java 后端技术⼀⾯⾯试原
题:讲⼀下 JVM 内存结构?
4. Java ⾯试指南(付费)收录的京东⾯经同学 1 Java 技术⼀⾯⾯试原题:说说
JVM 运⾏时数据区
5. Java ⾯试指南(付费)收录的美团⾯经同学 2 Java 后端技术⼀⾯⾯试原题:
JVM 内存结构了解吗?
6. Java ⾯试指南(付费)收录的快⼿⾯经同学 1 部⻔主站技术部⾯试原题:请说
⼀下 Java 的内存区域,程序计数器等?
7. Java ⾯试指南(付费)收录的字节跳动⾯经同学 8 Java 后端实习⼀⾯⾯试原
题:jvm 内存分布,有垃圾回收的是哪些地⽅
8. Java ⾯试指南(付费)收录的得物⾯经同学 8 ⼀⾯⾯试原题:说⼀说 jvm 内存
区域
No. 19 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
9. Java ⾯试指南(付费)收录的美团⾯经同学 3 Java 后端技术⼀⾯⾯试原题:
jmm 内存模型 栈 ⽅法区存放的是什么
10. Java ⾯试指南(付费)收录的收钱吧⾯经同学 1 Java 后端⼀⾯⾯试原题:你提
到了栈帧,那局部变量表除了栈帧还有什么?⼀个什么都没有的空⽅法,完全空
的参数什么都没有,那局部变量表⾥有没有变量?
11. Java ⾯试指南(付费)收录的招银⽹络科技⾯经同学 9 Java 后端技术⼀⾯⾯试
原题:Java堆内存和栈内存的区别
12. Java ⾯试指南(付费)收录的 OPPO ⾯经同学 1 ⾯试原题:说⼀下JVM内存模
型
13. Java ⾯试指南(付费)收录的深信服⾯经同学 3 Java 后端线下⼀⾯⾯试原题:
JVM变量存在堆栈的位置?
14. Java ⾯试指南(付费)收录的TP联洲同学 5 Java 后端⼀⾯的原题:Jvm内存区
域,本地⽅法栈的运⾏场景,Native⽅法解释⼀下
15. Java ⾯试指南(付费)收录的字节跳动同学 17 后端技术⾯试原题:jvm结构 运
⾏时数据区有什么结构 堆存什么
16. Java ⾯试指南(付费)收录的腾讯⾯经同学 29 Java 后端⼀⾯原题:new⼀个
对象存放在哪⾥?(运⾏时数据区),局部变量存在JVM哪⾥
4.说⼀下 JDK 1.6、1.7、1.8 内存区域的变化?
JDK 1.6 使⽤永久代来实现⽅法区:
No. 20 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
JDK 1.7 时仍然是永久带,但发⽣了⼀些细微变化,⽐如将字符串常量池、静态变量存放到了
堆上。
在 JDK 1.8 时,直接在内存中划出了⼀块区域,叫元空间,来取代之前放在 JVM 内存中的永
久代,并将运⾏时常量池、类常量池都移动到了元空间。
No. 21 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
5.为什么使⽤元空间替代永久代?
客观上,永久代会导致 Java 应⽤程序更容易出现内存溢出的问题,因为它要受到 JVM 内存
⼤⼩的限制。
HotSpot 虚拟机的永久代⼤⼩可以通过 -XX:MaxPermSize 参数来设置,32 位机器默认的⼤
⼩为 64M,64 位的机器则为 85M。
⽽ J9 和 JRockit 虚拟机就不存在这种限制,只要没有触碰到进程可⽤的内存上限,例如 32
位系统中的 4GB 限制,就不会出问题。
主观上,当 Oracle 收购 BEA 获得了 JRockit 的所有权后,就准备把 JRockit 中的优秀功能移
植到 HotSpot 中。
如 Java Mission Control 管理⼯具。
No. 22 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
但因为两个虚拟机对⽅法区实现有差异,导致这项⼯作遇到了很多阻⼒。
考虑到 HotSpot 虚拟机未来的发展,JDK 6 的时候,开发团队就打算放弃永久代了。
JDK 7 的时候,前进了⼀⼩步,把原本放在永久代的字符串常量池、静态变量等移动到了堆
中。
JDK 8 就终于完成了这项移出⼯作,这样的好处就是,元空间的⼤⼩不再受到 JVM 内存的限
制,⽽是可以像 J9 和 JRockit 那样,只要系统内存⾜够,就可以⼀直⽤。
6. 对象创建的过程了解吗?
当我们使⽤ new 关键字创建⼀个对象时,JVM ⾸先会检查 new 指令的参数是否能在常量池
中定位到类的符号引⽤,然后检查这个符号引⽤代表的类是否已被加载、解析和初始化。如
果没有,就先执⾏类加载。
No. 23 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
如果已经加载,JVM 会为对象分配内存完成初始化,⽐如数值类型的成员变量初始值是 0,
布尔类型是 false,对象类型是 null。
接下来会设置对象头,⾥⾯包含了对象是哪个类的实例、对象的哈希码、对象的 GC 分代年
龄等信息。
最后,JVM 会执⾏构造⽅法 <init> 完成赋值操作,将成员变量赋值为预期的值,⽐如 int
age = 18 ,这样⼀个对象就创建完成了。
对象的销毁过程了解吗?
当对象不再被任何引⽤指向时,就会变成垃圾。垃圾收集器会通过可达性分析算法判断对象
是否存活,如果对象不可达,就会被回收。
垃圾收集器通过标记清除、标记复制、标记整理等算法来回收内存,将对象占⽤的内存空间
释放出来。
可以通过 java -XX:+PrintCommandLineFlags -version 和 java -XX:+PrintGCDetails
-version 命令查看 JVM 的 GC 收集器。
可以看到,我本机安装的 JDK 8 默认使⽤的是 Parallel Scavenge + Parallel Old 。
不同参数代表对应的垃圾收集器表单:
No. 24 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
新⽣代 ⽼年代 JVM参数
Serial Serial -XX:+UseSerialGC
Parallel Scavenge Serial -XX:+UseParallelGC -XX:-UseParallelOldGC
Parallel Scavenge Parallel Old -XX:+UseParallelGC -XX:+UseParallelOldGC
Parallel New CMS -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
G1 -XX:+UseG1GC
1. Java ⾯试指南(付费)收录的⽐亚迪⾯经同学 3 Java 技术⼀⾯⾯试原题:对象
创建到销毁的流程
2. Java ⾯试指南(付费)收录的美团⾯经同学 2 Java 后端技术⼀⾯⾯试原题:说
说创建对象的流程?
3. Java ⾯试指南(付费)收录的携程⾯经同学 1 Java 后端技术⼀⾯⾯试原题:对
象创建到销毁,内存如何分配的,(类加载和对象创建过程,CMS,G1 内存清
理和分配)
7.堆内存是如何分配的?
在堆中为对象分配内存时,主要使⽤两种策略:指针碰撞和空闲列表。
No. 25 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
指针碰撞适⽤于管理简单、碎⽚化较少的内存区域,如年轻代;⽽空闲列表适⽤于内存碎⽚
化较严重或对象⼤⼩差异较⼤的场景如⽼年代。
什么是指针碰撞?
假设堆内存是⼀个连续的空间,分为两个部分,⼀部分是已经被使⽤的内存,另⼀部分是未
被使⽤的内存。
在分配内存时,Java 虚拟机会维护⼀个指针,指向下⼀个可⽤的内存地址,每次分配内存
时,只需要将指针向后移动⼀段距离,如果没有发⽣碰撞,就将这段内存分配给对象实例。
什么是空闲列表?
No. 26 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
JVM 维护⼀个列表,记录堆中所有未占⽤的内存块,每个内存块都记录有⼤⼩和地址信息。
当有新的对象请求内存时,JVM 会遍历空闲列表,寻找⾜够⼤的空间来存放新对象。
分配后,如果选中的内存块未被完全利⽤,剩余的部分会作为⼀个新的内存块加⼊到空闲列
表中。
1. Java ⾯试指南(付费)收录的携程⾯经同学 1 Java 后端技术⼀⾯⾯试原题:对
象创建到销毁,内存如何分配的,(类加载和对象创建过程,CMS,G1 内存清
理和分配)
memo:2025 年 1 ⽉ 10 ⽇修改到此
8.new 对象时,堆会发⽣抢占吗?
会。
new 对象时,指针会向右移动⼀个对象⼤⼩的距离,假如⼀个线程 A 正在给字符串对象 s 分
配内存,另外⼀个线程 B 同时为 ArrayList 对象 l 分配内存,两个线程就发⽣了抢占。
JVM 怎么解决堆内存分配的竞争问题?
为了解决堆内存分配的竞争问题,JVM 为每个线程保留了⼀⼩块内存空间,被称为 TLAB,
也就是线程本地分配缓冲区,⽤于存放该线程分配的对象。
No. 27 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
当线程需要分配对象时,直接从 TLAB 中分配。只有当 TLAB ⽤尽或对象太⼤需要直接在堆
中分配时,才会使⽤全局分配指针。
这⾥简单测试⼀下 TLAB。
可以通过 java -XX:+PrintFlagsFinal -version | grep TLAB 命令查看当前 JVM 是否开
启了 TLAB。
如果开启了 TLAB,会看到类似以下的输出,其中 bool UseTLAB 的值为 true。
我们编写⼀个简单的测试类,创建⼤量对象并强制触发垃圾回收,查看 TLAB 的使⽤情况。
No. 28 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
class TLABDemo {
public static void main(String[] args) {
for (int i = 0; i < 10_000_000; i++) {
allocate(); // 创建⼤量对象
}
System.gc(); // 强制触发垃圾回收
}
private static void allocate() {
// ⼩对象分配,通常会使⽤ TLAB
byte[] bytes = new byte[64];
}
}
在 VM 参数中添加 -XX:+UseTLAB -XX:+PrintTLAB -XX:+PrintGCDetails -
XX:+PrintGCDateStamps ,运⾏后可以看到这样的内容:
waste:未使⽤的 TLAB 空间。
alloc:分配到 TLAB 的空间。
refills:TLAB 被重新填充的次数。
可以看到,当前线程的 TLAB ⽬标⼤⼩为 10,496 KB( desired_size: 10496KB );未发⽣
慢分配( slow allocs: 0 );分配效率直接拉满( alloc: 1.00000 52494KB )。
当使⽤ -XX:-UseTLAB -XX:+PrintGCDetails 关闭 TLAB 时,会看到类似以下的输出:
No. 29 / 123
⾯渣逆袭 JVM篇第⼆版-让天下所有的⾯渣都能逆袭
直接出现了两次 GC,因为没有 TLAB,Eden 区更快被填满,导致年轻代 GC。年轻代 GC 频
繁触发,⼀部分⻓⽣命周期对象被晋升到⽼年代,间接导致⽼年代 GC 触发。
9.能说⼀下对象的内存布局吗?
好的。
对象的内存布局是由 Java 虚拟机规范定义的,但具体的实现细节各有不同,如 HotSpot 和
OpenJ9 就不⼀样。
就拿我们常⽤的 HotSpot 来说吧。
对象在内存中包括三部分:对象头、实例数据和对⻬填充。
No. 30 / 123