Java面试经验总结之JVM篇(一)

发布于:2024-08-10 ⋅ 阅读:(56) ⋅ 点赞:(0)

一、知识点汇总

JVM是Java运行基础,卖你是是一定对遇到JVM的相关问题,内容相对集中,但是知识深度要求较高

 其中内存模型,类加载机制,GC是重点。性能调优方面更偏向于应用,重点突出实践能力。编译器优化和执行模式偏向于理论基础,重点掌握知识点。

需了解:

内存模型 各部分作用 , 保存哪些数据 .
类加载 双亲委派加载机制 , 常用加载器分别加载哪种类型的类 .
GC 分代回收的思想和依据以及不同垃圾回收算法的回收思路和适合场景 .
性能调优 常有 JVM 优化参数作用 , 参数调优的依据 , 常用的 JVM 分析工具能分析哪些问题以及使用方法 .

 二、知识点详解:

1.JVM内存模型:

线程独占 : , 本地方法栈 , 程序计数器 线程共享 : , 方法区

1.1 

又称方法栈 , 线程私有的 , 线程执行方法是都会创建一个栈阵 , 用来存储局部变量表 , 操作栈 , 动态链接 , 方 法出口等信息. 调用方法时执行入栈 , 方法返回式执行出栈 .

1.2 本地方法栈

与栈类似 , 也是用来保存执行方法的信息 . 执行 Java 方法是使用栈 , 执行 Native 方法时使用本地方法栈 .

1.3 程序计数器

保存着当前线程执行的字节码位置 , 每个线程工作时都有独立的计数器 , 只为执行 Java 方法服务 , 执行
Native 方法时 , 程序计数器为空

1.4 堆

JVM 内存管理最大的一块 , 对被线程共享 , 目的是存放对象的实例 , 几乎所欲的对象实例都会放在这里 ,
当堆没有可用空间时 , 会抛出 OOM 异常 . 根据对象的存活周期不同 ,JVM 把对象进行分代管理 , 由垃圾回 收器进行垃圾的回收管理

1.5 方法区

又称非堆区 , 用于存储已被虚拟机加载的类信息 , 常量 , 静态变量 , 即时编译器优化后的代码等数据 .1.7
的永久代和 1.8 的元空间都是方法区的一种实现

2. 类加载

加载过程

 

其中 验证 , 准备 , 解析 合称链接
加载 通过类的完全限定名 , 查找此类字节码文件 , 利用字节码文件创建 Class 对象 .
验证 确保 Class 文件符合当前虚拟机的要求 , 不会危害到虚拟机自身安全 .
准备 进行内存分配 , static 修饰的类变量分配内存 , 并设置初始值 (0 null). 不包含 final 修饰的静态变
, 因为 final 变量在编译时分配 .
解析 将常量池中的符号引用替换为直接引用的过程 . 直接引用为直接指向目标的指针或者相对偏移量等.
初始化 主要完成静态块执行以及静态变量的赋值 . 先初始化父类 , 再初始化当前类 . 只有对类主动使用
时才会初始化 . 触发条件包括, 创建类的实例时 , 访问类的静态方法或静态变量的时候 , Class.forName 反射类的时 候, 或者某个子类初始化的时候 .
Java 自带的加载器加载的类 , 在虚拟机的生命周期中是不会被卸载的 , 只有用户自定义的加载器加载的 类才可以被卸.

2.1 类加载器

1. 启动类加载器(Bootstrap ClassLoader)
  • 职责:加载 Java 核心类库,如java.lang.*、java.util.*等。
  • 实现:由本地代码(通常是 C++)实现,不是java.lang.ClassLoader的子类。
  • 加载路径:$JAVA_HOME/jre/lib/rt.jar或jrt:/modules(在模块化系统中)。
  • 特点:是所有类加载器的顶层,没有父类加载器。
2. 扩展类加载器(Extension ClassLoader)
  • 职责:加载扩展库中的类。
  • 实现:由sun.misc.Launcher$ExtClassLoader实现,是java.lang.ClassLoader的子类。
  • 加载路径:$JAVA_HOME/jre/lib/ext目录或由java.ext.dirs系统属性指定的目录。
  • 父类加载器:引导类加载器。
3. 应用程序类加载器(Application ClassLoader)
  • 职责:加载应用程序类路径(classpath)中的类。
  • 实现:由sun.misc.Launcher$AppClassLoader实现,是java.lang.ClassLoader的子类。
  • 加载路径:由java.class.path系统属性指定的目录和 JAR 文件。
  • 父类加载器:扩展类加载器。
4. 自定义类加载器(Custom ClassLoader)
  • 职责:满足特定需求的类加载器,通常在应用程序中自定义实现。
  • 实现:继承java.lang.ClassLoader并重写findClass方法。
  • 加载路径:由开发者自行定义,可以是文件系统、网络、数据库等。
  • 父类加载器:可以指定,也可以继承应用程序类加载器。

2.2 双亲委派机制

双亲委派模式 , 即加载器加载类时先把请求委托给自己的父类加载器执行 , 直到顶层的启动类加载器 .
父类加载器能够完成加载则成功返回 , 不能则子类加载器才自己尝试加载 .
双亲委派机制的工作流程:
  1. 启动类加载器(Bootstrap ClassLoader):负责加载 Java 核心库(位于JAVA_HOME/lib目录下的类库,如rt.jar)。
  2. 扩展类加载器(Extension ClassLoader):负责加载 Java 扩展库(位JAVA_HOME/lib/ext目录下的类库)。
  3. 应用程序类加载器(Application ClassLoader):负责加载应用程序类路径(classpath上的类。

 具体流程为:

  1. 当前类加载器收到类加载请求:当一个类加载器收到加载类的请求时,它不会立即尝试加载该类。
  2. 将请求委派给父类加载器:当前类加载器首先将加载请求委派给父类加载器。
  3. 父类加载器处理请求
  • 如果父类加载器存在,则父类加载器会继续将请求向上委派,直到到达启动类加载器。
  • 启动类加载器尝试加载类,如果成功,则返回类的引用。
  1. 父类加载器无法加载类:如果启动类加载器无法加载该类,加载失败返回到子类加载器。
  2. 当前类加载器尝试加载类:如果父类加载器无法加载该类,则由当前类加载器尝试加载。

3. JVM的垃圾回收机制 

3.1 垃圾回收机制是什么?

所谓垃圾回收机制(Garbage Collection, 简称GC),指自动管理动态分配的内存空间的机制,自动回收不再使用的内存,不定时去堆内存中清理不可达对象,以避免内存泄漏和内存溢出的问题。最早是在1960年代提出的。

垃圾回收是 java相较于c、c++语言的优势之一。其他编程语言,如C#、Python和Ruby等,也都提供了垃圾回收机制。不可达的对象并不会马上就会直接回收, 垃圾收集器在一个Java程序中的执行是自动的,不能强制执行,程序员唯一能做的就是通过调用System.gc 方法来建议执行垃圾收集器,但其是否可以执行,什么时候执行却都是不可知的。

这也是垃圾收集器的最主要的缺点。

3.2垃圾回收流程

1. 垃圾分类

首先我们的 jvm 在进行垃圾回收的过程,需要确定哪些对象是垃圾对象,哪些对象是存活对象。

一般有三种常用算法可以将对象分类:

        1.引用计数法:

        引用计数法(Reference Counting)是一种内存管理技术,用于跟踪对象的引用数量。每个对象都有一个引用计数器,记录着指向该对象的引用数量。当一个对象被引用时,引用计数器加一;当一个引用被释放时,引用计数器减一。当引用计数器为零时,表示没有任何引用指向该对象,该对象可以被释放,回收其占用的内存。

        2.可达性分析法:

        可达性分析算法是JVM垃圾回收中的一种算法,它通过分析对象的引用关系,判断对象是否可达,从而决定对象是否可以被回收。

        3.三色标记算法(并行环境)

        三色标记算法是一种用于垃圾回收的标记算法,通过将对象分为三种颜色(白色、灰色和黑色)来管理垃圾收集过程。它主要解决了在并发垃圾收集过程中如何正确标记存活对象的问题,避免了遗漏存活对象或错误标记对象的情况。

        3.对比

        引用计数法通过维护引用计数器来跟踪对象的引用数量,具有实时性好、简单高效等优点,但存在循环引用等问题;而可达性分析法则通过分析对象的引用关系来判断对象是否可达,从而决定对象是否可以被回收,具有准确性高、效率好等优点,是JVM中常用的垃圾回收算法之一。

 2.垃圾查找及垃圾清理

当程序运行时达到一定条件就会触发特定的垃圾回收算法,垃圾收集器会将所有的被标记过的不可达对象清理。

常用的垃圾回收算法:
1. 标记-清除算法
  • 原理:垃圾收集器首先遍历对象图,标记所有可达的对象,然后清除未标记的对象。
  • 优点:简单直接,不需要移动对象。
  • 缺点:会产生内存碎片,可能导致大对象分配失败。
2. 标记-压缩算法
  • 原理:在标记阶段标记所有可达的对象后,压缩阶段将存活的对象移动到内存的一端,整理出连续的可用内存空间。
  • 优点:消除了内存碎片问题。
  • 缺点:对象移动需要额外的时间和资源。
3. 复制算法
  • 原理:将内存分为两个相等的区域,每次只使用其中一个。当这个区域使用完时,将存活的对象复制到另一个区域,然后清空当前区域。
  • 优点:简单高效,没有内存碎片问题。
  • 缺点:需要双倍的内存空间。
4. 分代收集算法
  • 原理:根据对象的生命周期将堆内存划分为几代(通常是新生代和老年代),新生代使用复制算法,老年代使用标记-压缩或标记-清除算法。
  • 优点:优化了垃圾收集性能,因为大部分对象在新生代被收集,减少了老年代的垃圾收集频率。
  • 缺点:需要额外的内存管理和调优。
5. 分区算法
  • 原理:将堆内存划分为多个小的独立区域(Region),每个区域可以独立进行垃圾收集。
  • 优点:提高了内存管理的灵活性和效率,适用于大堆内存的应用。
  • 缺点:实现较复杂,需要精细的内存管理。
6. 并行和并发收集算法
  • 并行收集:垃圾收集过程由多个线程并行执行,提高了垃圾收集的效率。
  • 并发收集:垃圾收集过程与应用线程并发执行,减少了应用停顿时间。
具体垃圾收集器使用的算法
  • Serial GC:使用标记-压缩算法。
  • Parallel GC:新生代使用复制算法,老年代使用标记-压缩算法。
  • CMS GC:新生代使用复制算法,老年代使用标记-清除算法,并发标记和清除。
  • G1 GC:分区算法,结合标记-压缩和复制算法。
  • ZGC:分区算法,使用染色指针和读屏障技术,实现并发标记和压缩。
  • Shenandoah GC:分区算法,使用并发标记和并发压缩技术。