JVM 知识体系极速通关:一文带你秒懂核心要点

这篇文章特别适合那些刚刚接触 Java 虚拟机(JVM)的新手,以及希望快速回顾 JVM 核心知识的同学。本文基于我个人的学习经验整理而成,旨在帮助大家更好地理解和记忆 JVM 的重要概念。跟着我的步骤来,相信大家的脑子里一定会对JVM相关的知识体系有个清晰的印象。


记忆流程:

当你点击运行按钮,我们编写的Java文件,会经过javac编译成.class文件(字节码文件)。字节码文件会被Classloader(类加载器) 加载运行时数据区(内存中)。

思考
操作系统能直接识别字节码文件吗?
不能!需要JVM的执行引擎担任解释器,将字节码解释成机器码。 因为字节码指令集属于JVM自己的一套指令规范,操作系统不能直接识别,所以需要这么一个解释器。
解释过程中会调用第三方语言(如C++)提供的本地方法接口(如驱动、地图制作等)。

上面的描述中,我们已经记住了JVM的几个核心组成

JVM核心组成

  1. 类加载器 (负责把类文件加载到内存中)
  2. 运行时数据区 (也是我们常说的内存)
  3. 执行引擎 (解释器)
  4. 本地方法接口

然后就从JVM的核心组成出发,顺序去深入了解JVM即可

类加载机制

1. 类加载流程:

  1. 加载
    查找并加载类文件到JVM中。
  2. 链接
    分三阶段:
    • 验证:检查字节码是否符合JVM规范。
    • 准备:为静态变量分配内存,赋初始值(如int为0)。
    • 解析:将常量池的符号引用转为直接引用(这里的符号引用我们举个例子,myclass类中,我写了个System.out.println(“HELLOW”),这里的System.out.println就像一个符号引用,大致意思就是 “隔壁老王要打印hellow”,解析之后,就是隔壁老王住在哪儿,我去找他让他打印hellow。这里举例子大家应该懂了,符号引用就像一个签名,我根据签名能找到对应的对象来帮我做事)
  3. 初始化
    执行静态代码块和静态变量赋值。

2. 双亲委派机制

双亲委派机制是JVM推荐的一种类加载机制,非强制性的哦~

  • 核心逻辑:加载时先委托父类加载器加载,父类无法加载时自己处理。
  • 优点:防止核心类被篡改。
  • 加载顺序:自下而上(子→父),父类加载器不记录子类,向上委托效率更高。

3. 类加载器分类

  1. 启动类加载器:加载lib下的核心类。
  2. 扩展类加载器:加载lib/ext的扩展类。
  3. 应用类加载器:加载用户编写的classpath下的类。

运行时数据区(内存)

这块儿建议大家结合自己写的java代码进行理解记忆,就很ez了。

1. 方法区

  • 存储内容:类信息、常量、静态变量(线程共享)。
  • 演进
    • JVM8前:由永久代实现(基于堆内存,可能OOM)。
    • JVM8后:由元空间实现(基于本地内存,降低OOM风险)。

这里我们多提一个即时编译器的概念,还记得执行引擎么,每次都会把我们的字节码指令转换为机器码,每次都要一行行读,我们是否可以把经常被执行的代码给“缓存”起来,便于下次直接使用,就不用解释了?

即时编译器(JIT)

  • 作用:缓存高频执行的字节码解释结果(直接复用机器码,提升效率)。
  • 存储位置:编译后的代码存入Code Cache(特殊内存空间,不属于方法区或堆)。 其实就是个特殊的内存空间,不属于任何地方,因为他“特殊”。

2. 虚拟机栈

  • 特性:线程私有,每个方法调用创建一个栈帧
  • 栈帧内容:局部变量表、操作数栈、动态链接、方法出口。
  • 风险:大部分的stackoverflow都是因为自己的写法有问题导致无限递归,如果是因为内存不足,可以配置对应的栈内存。

3. 堆

  • 特性:线程共享,存储所有对象和数组(GC主战场)。

  • 垃圾回收算法

    1. 标记清除法
      • 流程:标记存活对象→清除未标记对象。
      • 缺点:内存碎片。
    2. 标记整理法
      • 流程:标记存活对象→清除未标记对象→整理内存。
      • 缺点:效率低。
    3. 复制算法
      • 流程:将存活对象复制到另一块内存→清空原内存。
      • 缺点:内存利用率50%(需预留一半空间)。
  • 堆内存划分

    1. 新生代
      • Eden区(80%):新对象创建区。
      • Survivor区(S0/S1各10%):采用复制算法(内存利用率90%)。

    这里解释下,新生代是新生JAVA对象玩的地方,复制算法的核心是空间分成两半,但是那样空间的利用率只有百分之50,因为还有一半要用来放可用对象,可如果我每次都使用Eden区和S区的任意一个比如S0,做垃圾回收的时候,把可用对象放到S1区,清除伊甸和S0,然后在用伊甸和S1,做垃圾回收的时候把可用对象再放到S0,以此往复,利用率就有百分之90

    这里会产生很多有趣的问题,比如有同学会问,假如可用的大对象在S1放不下咋办? 放到老年代做兜底

    可能又有同学会问,假如老年代也放不下咋办? 那就根据当前的垃圾回收器,做可以对老年代的垃圾回收,然后再去放,比如CMS,做FullGC,对新老年代做垃圾回收,然后在放大对象。

    可能又又有同学会问,假如还是放不下咋办,还能咋办? OOM了呗!

    1. 老年代
      • 长期存活对象存放区(大对象直接进入老年代)。

现成的著名的垃圾回收器

  • CMS:低停顿,标记清除。
  • G1:分代+分区,平衡延迟与吞吐。
  • ZGC:超低停顿(TB级堆)。

4. 本地方法栈

  • 功能:为JNI调用的本地方法分配内存(类似虚拟机栈,线程私有)。

5. 程序计数器

  • 特性:线程私有,唯一无OOM区域。
  • 作用:记录当前线程执行的指令位置(便于CPU切换后恢复)。

原文补充细节

  1. 类加载解析阶段:符号引用包含类和方法的信息签名,解析后转为直接引用(内存地址)。
  2. 堆内存分配
    • 伊甸区与Survivor区比例默认8:1:1
    • Survivor区放不下的对象直接进入老年代。
  3. 垃圾回收触发
    • 老年代空间不足时触发Full GC。
  4. JVM设计权衡:不同垃圾回收器适用于不同场景(如CMS注重低延迟,G1平衡吞吐和延迟)。
  5. OOM场景
    • 方法区(元空间):加载过多类。
    • 堆:创建大对象或内存泄漏。
    • 虚拟机栈:无限递归。

过知识点的总体流程:

从我写java代码开始,到被javac编译成字节码文件,会被类加载器加载到内存

这里我们可以深究JVM的组成 1 : 类加载器,我们可能会联想 类加载器的加载过程,类加载器常见的有哪些?双亲委派机制等等?

还能深究JVM的组成2,内存,也是运行时数据区,我们可能又会联想到运行时数据区分那几部分? 每部分的主要存储啥?方法区不同JVM的实现? 堆?垃圾回收? 常见的垃圾回收算法? 年轻代里面为啥伊甸区:S0:S1常见比例为8:1:1? 哪几种情况对象会进入到老年代? 常见的垃圾回收器和特点等等? 我上面问的这些其实都是按流程顺序思考联想的问题。内存这块儿是内容最多的,所以产生的问题碰撞也是最多的,大家后续可以深究下。

字节码文件不能被操作系统直接运行,需要执行引擎解释一下成机器码,然后cpu去执行。这个期间会有其他第三方语言如C++提供的本地方法接口做辅助(驱动、地图制作等)。

其实这样在过JVM主要的组成部分的时候,其实就能把JVM大部分的内容都能过一遍了。按照这套流程走下来,基本上能够涵盖 JVM 的大部分核心知识点。在走这套无比丝滑,无比德芙的记忆流程的时候,你会发现所有关键的知识点都自然浮现出来。然后针对性去深究即可吊打面试官!

分享我的 JVM 复习流程,希望能够帮助到大家。如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和关注,后续我还会继续分享更多类似的复习总结和实用技巧哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值