美文网首页
JVM 结构

JVM 结构

作者: GeekAmI | 来源:发表于2025-03-19 15:58 被阅读0次
1、类加载机制?

类加载指的是 JVM 通过类加载器,把.class文件加载到方法区,并在JVM堆区建一个 java.lang.Class的 实例,用来封装 Java 类相关数据和方法。一般说的类加载机制,指的是双亲委派机制,即如果一个类加载器加载了一个类,它首先不是自己去加载这个类,而是把请求委派给父加载器,以此类推...。如果父加载器加载不了,它自己才会加载,自己也加载不了就抛出ClassNotFoundException。
JVM 判定一个类是否相同有 2 个条件:1 、包+类名一致;2 、同一个类加载器加载的
类加载器从顶到下分别为 Bootstrap ClassLoader(启动类加载器)、 ExtClassLoader(扩展类加载器)、AppClassLoader(应用类加载器)

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }
2、如何打破双亲委派机制?

A:继承ClassLoad抽象类,覆写loadClass方法。Tomcat 通过自定义WebAppClassLoader,优先加载Web 应用目录下的类,然后再加载其他目录下的类,进而打破双亲委派机制。具体参加:https://time.geekbang.org/column/article/105110

3、双亲委派机制的好处?

A:一是避免同类(指的同包+同名+同类加载器实例)被加载多次,二是因为优先加载父类加载器,保护 Java 核心 api的类不会被篡改。

4、类加载过程或类的生命周期?

A:类的生命周期分为 Loading(加载)、 Linking(连接)、Initialization(初始化)、Using(使用)、 UnLoading(卸载)阶段。

类加载过程分 3 步:加载->连接->初始化,其中连接过程又分为 3 步:验证、准备、解析。

  • 加载:类加载器通过类全名获取类二进制字节流;将字节流所代表的静态存储结构,转化为方法区的运行时数据结构;在内存生成代表该类的 Class对象,作为方法区这些数据的访问入口。
  • 验证:是连接阶段的第一步,目的是确认加载的Class 文件字节流符合《Java 虚拟机规范》,主要验证点包含Class文件格式检查、元数据验证(字节码语义)、字节码验证(程序语义)、符号引用验证(类的正确性检查)
  • 准备:为类变量分配内存,并为其设置初始值。需要注意的是,从 JDK1.7 及以后版本,HotSpot已经将字符串常量池和类静态变量等移动到堆中,这时候类变量会随着 Class 对象一起放到堆中
  • 解析:JVM 将常量池内的符号引用替换为直接引用的过程,也就是得到类或字段、方法在内存的地址或偏移量
  • 初始化:是直接<clinit>方法的过程,是类加载的最后一步,这一步JVM 才开始执行类中定义Java程序代码(字节码)

类卸载过程:即该类的 Class 对象被 GC 。需满足 3 个条件:该类的所有实例都已被 GC,即堆里不存在该类的实例对象;该类没在其他任何地方引用;该类的类加载器已被 GC。

5、介绍下 Java 内存区域(运行时数据区)

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

  • 程序计数器(Program Counter Register):存储下个指令的地方,无垃圾回收、无 OOM
  • 虚拟机栈(VM Stack):一句话理解,栈是用来解决程序运行问题的;二句话理解,调用方法,创建栈帧,入栈,方法结束,出栈。栈帧是一块内存区域,维系着方法执行的各种数据,包含局部变量表、操作数栈、动态链接、方法返回地址。常见问题:若栈的大小不允许动态扩展,超过设置的固定大小,报StackOverFlowError;若栈允许动态拓展,没有空闲的内存空间时,报 OutOfMemoryError
  • 本地方法栈(Native Method Stack):与虚拟机栈作用类似,也会出现StackOverFlowError和 OutOfMemoryError。虚拟机栈是为 Java 程序服务的,本地方法栈是虚拟机使用的 native 方法服务
  • 方法区:虚拟机使用一个类时,会把 Class字节码文件加载到方法区存储。方法区会存储类信息、字段信息、方法信息、 JIT 代码缓存、运行时常量等。永久代和元空间是 HotSpot对方法区的两种实现,永久代是 JDK1.8 之前的实现,元空间是 JDK1.8 及以后的实现。元空间使用的本地内存,受本机内存限制,元空间溢出时,报OutOfMemoryError
  • 堆(Head):一句话,解决对象分配存放的问题。从JDK1.7,字符串常量池和静态变量存储在堆。堆分为新生代(Young Generation)、老年代(Old Generation),其中新生代包括 Eden 、 S0 、S1。两种情况会出现OutOfMemoryError,当 JVM 花太多时间执行 Full GC,但却只能回收少量空间;或堆空间不足以存放新对象。

Q:常见概念
A:新生代垃圾回收:Minor GC/Young GC;老年代垃圾回收:Major GC;整堆垃圾回收:Full GC,回收整个堆和方法区;混合收集(Mix):Mix GC, G1中的概念,收集整个新生代和部分老年代

6、对象在堆上分配的过程?

A:情况 1:对象首先会分配到 Eden 区;情况 2:如果是大对象,直接进入老年代;如果 Eden 容纳不下,发生一次 minor GC,Eden 和 S0 一起转移到 S1区,此时仍有存活对象(S1 区),其年龄+1;下次 Eden 又满了, 发生一次minor GC,Eden+S1进入 S0,年龄又+1,当年龄大于阈值,进入老年代。

7、进入老年代的情况?

A:大对象直接进入老年代;长期存活的对象进入老年代;对象年龄动态判断,s区相同年龄的对象大小总和>s区空间一半,那么大于等于此年龄的对象进入老年代;minor GC过程中如果 S 区容纳不下,则通过分配担保机制进入老年代。

8、堆、栈、方法区的关系?

A:对象引用分配到栈,对象分配到堆,类信息存储在方法区

9、Java 对象的创建过程
  • 步骤 1、类加载检查
    虚拟机遇到new指令时,首先检查常量池中是否存在类的符号引用,若未加载则触发类加载流程(加载.class文件到方法区,解析并初始化类元数据)。若类已加载,则直接进入内存分配阶段。
    关键点:
    符号引用:编译时用类全限定名代替实际内存地址,类加载时解析为元数据指针。
    懒加载机制:仅当使用类时才会加载。
  • 步骤 2、内存分配
    在堆中为对象划分内存空间,具体方式取决于堆内存是否规整:
    指针碰撞(Bump The Pointer),堆内存规整(已用内存与空闲内存分离),通过移动分界指针快速分配空间。适用场景:使用Serial、ParNew等带压缩功能的垃圾收集器。
    空闲列表(Free List),堆内存不规整时,维护空闲块列表,按需分配并更新列表。适用场景:CMS等不带压缩功能的收集器。
    并发安全处理:
    CAS + 重试:通过原子操作保证指针移动的线程安全。
    TLAB(线程本地分配缓冲):为每个线程预分配堆内存区域,减少竞争。
  • 步骤 3、初始化零值
    将分配的内存空间初始化为默认值(如int为0,引用类型为null),确保未显式赋值的字段可安全使用。若使用TLAB,此步骤可能提前执行。
  • 步骤 4、设置对象头
    在对象头中存储元数据信息,包括:类型指针:指向类的元数据(如class对象)。哈希码:对象内存首地址的哈希值(默认)。GC信息:分代年龄、锁状态等。对象布局:对象头(含Mark Word和元数据指针)、实例数据、对齐填充。
  • 步骤 5、执行构造方法(方法)
    从Java程序视角,对象初始化正式开始:调用构造函数,执行字段显式赋值、实例初始化块等操作。将堆内存地址赋值给引用变量,完成对象可用性。
  • 步骤 6、返回对象引用
    将对象的内存地址返回给变量,供后续使用。
  • 总结:Java对象创建流程可概括为:类加载 → 内存分配 → 零值初始化 → 对象头设置 → 构造方法执行 → 返回引用。核心难点在于内存分配策略(指针碰撞/空闲列表)和并发安全机制(CAS/TLAB),需结合垃圾收集器特性理解。掌握此流程有助于优化内存管理和排查内存泄漏问题。
10、对象的访问定位的两种方式(句柄和直接指针两种方式)
  • 方式 1:句柄访问(Handle)
[ 栈(Stack) ]  
| 引用变量 → [ 句柄池(Handle Pool) ] → [ 对象实例数据 ]  
|          |  
|          → [ 类元数据(Class Data) ] (存储在方法区)  

图示说明:

  • 栈:存储引用变量,指向句柄池中的句柄。
  • 句柄池:包含对象实例数据地址和类型指针。
  • 对象实例数据:堆中的实际对象内容。
  • 类元数据:方法区中的类信息(如字段、方法)。
    特点:
  • 引用地址稳定,对象移动时只需修改句柄中的实例指针。
  • 额外一次间接寻址,性能略低。

方式 2:直接指针(Direct Pointer)

[ 栈(Stack) ]  
| 引用变量 → [ 对象头(Header) ] → [ 类元数据(Class Data) ]  
|          [ 实例数据(Instance Data) ]  
|          [ 对齐填充(Padding) ]  

图示说明:

  • 栈:引用变量直接指向堆中的对象头。
  • 对象头:包含类型指针(指向方法区类元数据)和哈希码等元信息。
  • 实例数据:对象的实际字段内容。
    特点:
  • 一次直接寻址,访问速度快。
  • 对象移动时需更新所有相关引用地址。

HotSpot虚拟机使用的直接指针。

具体图例可参考:https://javaguide.cn/java/jvm/memory-area.html#%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AE%BF%E9%97%AE%E5%AE%9A%E4%BD%8D

参考文档

相关文章

  • JVM结构书目录

    JVM结构 JVM结构之运行时数据区 JVM结构之执行引擎 JVM结构之类加载子系统 JVM结构之本地方法接口(J...

  • 深入JVM内核原理-2.JVM运行机制

    1.JVM启动流程 JVM启动流程.png 2.JVM基本结构 JVM基本结构.png PC寄存器每个线程拥有一个...

  • JVM结构、GC工作机制

    JVM结构、内存分配、垃圾回收算法、垃圾收集器。 一、JVM结构 JVM的基本结构一般如下图所示: 从左图可知,J...

  • JVM内存结构和Java内存模型

    最近看到两个比较容易混淆的概念:JVM内存结构和Java内存模型 JVM内存结构JVM内存结构或者说内存模型指的是...

  • JVM-02

    JVM内存结构

  • JVM 内存结构解析

    1. JVM内存结构 (1) JDK1.7的JVM内存结构 JVM内存结构主要有三大块:堆内存、方法区和栈。 堆内...

  • JVM学习笔记

    一、JVM的结构图 1.1 Java内存结构 JVM内存结构主要有三大块:堆内存、方法区和栈。堆内存是JVM中最大...

  • JVM

    JVM(1):Java 类的加载机制 JVM(2):JVM内存结构 JVM(3):Java GC算法 垃圾收集器 ...

  • JVM(七):JVM内存结构

    JVM(七):JVM内存结构 在前几节的文章我们多次讲到 Class 对象需要分配入 JVM 内存,并在 JVM ...

  • JVM基础结构与字节码执行引擎

    JVM基础结构 JVM内部结构如下:栈、堆。 栈 JVM中的栈主要是指线程里面的栈,里面有方法栈、native方法...

网友评论

      本文标题:JVM 结构

      本文链接:https://www.haomeiwen.com/subject/cepgmjtx.html