Skip to content

Java 虚拟机 已完结 ​

JVM ​

说一下 JVM 的内存结构 ​

提示

堆、虚拟机栈、本地方法栈、方法区、程序计数器

  1. 方法区:类对象、常量、常量池、静态变量
  2. 堆:存储对象实例
  3. 虚拟机栈
  4. 本地方法栈
  5. 程序计数器:存储下一条指令地址

说一下堆和栈的区别 ​

提示

个数、内容、物理地址、生命周期

  1. 内存共享:每个线程都有各自的栈,所有线程共享一个堆。
  2. 存储内容:栈存放临时变量、操作数等,堆存放对象实例。
  3. 生命周期:栈跟随线程,堆跟随整个程序
  4. 物理地址:栈的地址是连续的,堆的地址是分散的。

内存溢出和内存泄漏的区别 ​

  1. 内存泄漏:申请了内存没有释放
  2. 内存溢出:没有足够的内存去使用

栈溢出的原因 ​

  1. 设置的栈内存过小
  2. 方法中存在错误,比如无限递归

方法区溢出的原因 ​

  1. 使用动态代理生成了过多的类
  2. 设置的方法区内存过小

运行时常量池溢出的原因 ​

  1. 常量池设置的的内存过小
  2. 大量调用了 intern 方法

什么是类加载?类加载的过程? ​

什么是类加载:将 class 文件读入内存,存放在 JVM 的方法区中的过程。

类加载的过程:加载、连接(验证、准备、解析)、初始化、使用、卸载。

img

什么是类加载器?类加载器有哪些? ​

类加载器:用于通过类的全限定名获取 Class 对象

类加载器:

  • 启动类加载器
  • 扩展类加载器
  • 应用程序加载器
  • 自定义加载器

什么是双亲委派模型? ​

一个类加载器收到类加载请求时,会先委托父类加载器完成加载,直至启动类加载器。只有父加载器无法完成加载时,会让子类加载器尝试加载。

为什么需要双亲委派模型? ​

  1. 保证 Java 的核心代码不会被恶意替换,保证代码安全。
  2. 避免一个类被重复加载

怎么判断两个类是否相等? ​

  1. 来源于一个 Class 文件(相同限定名)
  2. 被同一个类加载器加载

类的实例化顺序? ​

  1. 父类 static
  2. 当前类 static
  3. 父类普通代码
  4. 父类构造方法
  5. 当前类普通代码
  6. 当前类构造方法

Java 对象的定位/访问方式 ​

提示

直接指针:引用指向堆中的地址

句柄:维护一个映射表,表中存放堆中的地址

Java 通过指向对象的引用来操作对象,访问方式分为句柄和直接指针两种。

  1. 直接指针:引用指向堆中对象的地址。优点是访问速度快。(Hotspot 虚拟机使用这种)
  2. 句柄:引用指向堆中的一个句柄,句柄里存放真实对象的地址。优点是对象移动改变地址时,不需要修改对象引用的地址。

如何判断一个对象是否存活? ​

提示

引用计数法、可发性分析

  1. 引用计数法:记录这个对象被引用的次数
  2. 可达性分析:通过存在的引用,分析是否能访问到该对象

GC 是什么?为什么需要 GC? ​

GC 是垃圾回收机制。

为什么需要 GC:内存回收非常容易出问题,Java 提供的 GC 可以实现自动回收内存。

可作为 GC Roots 的对象有什么? ​

  1. 栈中的引用的对象
  2. 静态属性引用的对象
  3. synchronized 持有的对象
  4. 常量引用的对象
  5. Native 方法引用的对象
  6. JVM 内部引用的对象

什么情况下类会被卸载? ​

  1. 所有实例已经被回收
  2. 加载该类的类加载器已经被回收
  3. 该类的类对象没有被引用,不能通过反射访问该类

可以对满足上述 3 个条件的类进行回收,但不一定会进行回收。

强引用、软引用、弱引用、虚引用是什么? ​

  1. 强引用:对象不会被回收。
  2. 软引用:内存不足时回收,常用于缓存机制。
  3. 弱引用:垃圾回收器运行时回收。
  4. 虚引用:用于跟踪对象回收状态。

聊聊 Java 的 GC,分别作用在什么范围? ​

  1. Young GC:新生代
  2. Old GC:老年代
  3. Full GC:整个堆
  4. Mixed GC(混合收集):新生代和部分老年代(G1 GC)

JVM 的内存分配策略 ​

  • 对象优先在 Eden(伊甸园区)分派
  • 大对象和长期存活的对象直接进入老年代
  • 动态对象年龄判定
  • 空间分配担保

Full GC 的触发条件 ​

  1. 主动调用 System.gc()
  2. 老年代空间不足
  3. 空间分配担保失败

垃圾回收算法有那些? ​

  1. 标记清除:通过标记不需要的对象,再统一清除。
  2. 复制清除:将存活对象复制到新空间,旧空间的对象全部清除。
  3. 标记整理:标记存活对象,再整理存活对象以消除碎片。
  4. 分类收集:将对象分为不同代,根据对象的生命周期选择不同的回收策略。

有哪些垃圾回收器? ​

对象头了解吗? ​

对象由对象头、实例数据、对齐填充字节组成。

对象头分为 MarkWord、指向类对象(Class 对象)的指针、数组长度(只有数组有)组成。

MarkWord 包含对象哈希码、GC 分代年龄和锁标志位。

对齐填充字节:JVM 要求对象占的内存大小是 8 byte 的倍数,因此后面有几个字节用于把对象的大小补齐至 8 byte 的倍数。

main 方法的执行过程 ​

java
public class Application {
    public static void main(String[] args){
        Person p = new Person("wmh");
        p.getName();
    }
}

class Person {
    public string name;

    public Person(String name){
        this.name = name;
    }

    public string getName(){
        return this.name;
    }
}
  1. 编译 Application.java 得到 class 文件,运行 class 文件得到一个 JVM 进程。从类路径中找到 Application.class 的二进制文件,将类信息加载到方法区中(类的加载)
  2. JVM 从 main 方法开始运行
  3. Person p = new Person("wmh");:加载 Person 类,放入方法区。在堆中申请内存,初始化对象,执行构造方法。
  4. p.getName();:根据 p 的引用找到 p 对象,根据 Person 类对象的方法表获得 getName 方法,并执行。

对象创建过程 ​

  1. 类加载检查:查看类对象是否被加载
  2. 分配内存
  3. 初始化零值:将分配的内存置为零值
  4. 设置对象头:在对象头标记是哪个类的实例、对象哈希码、GC 分代年龄等
  5. 执行 init 方法:根据构造方法初始化对象

Released under the MIT License.