1. 首页 > 电脑知识

八股—7.JVM 八股编绳教程

作者:admin 更新时间:2025-06-14
摘要:1. JVM组成 1.1 JVM由哪些部分组成?运行流程?(讲一下JVM)⭐️ 难易程度:☆☆☆出现频率:☆☆☆☆ Java Virtual Machine:Java 虚拟机,Java程序的运行环境(java二进制字节码的运行环境) 好处:一次编写,到处运行;自动内存管理,垃圾回收机制 程序运行之前,需,八股—7.JVM 八股编绳教程

 

1. JVM组成

1.1 JVM由哪些部分组成?运行流程?(讲一下JVM)⭐️

难易程度:☆☆☆        出现频率:☆☆☆☆

Java Virtual Machine:Java 虚拟机,Java程序的运行环境(java二进制字节码的运行环境) 好处:一次编写,到处运行;自动内存管理,垃圾回收机制

程序运行之前,需要先通过编译器将 Java 源代码文件编译成 Java 字节码文件;

程序运行时,JVM 会对字节码文件进行逐行解释,翻译成机器码指令,并交给对应的操作 体系去执行。

好处:一次编写,到处运行;自动内存管理,垃圾回收机制

JVM  <—> 操作 体系(windows、linux)<—> 计算机硬件(cpu、内存条) java跨平台是因JVM屏蔽了操作 体系的差异,真正运行代码的不是操作 体系

JVM 主要由四个部分组成: 运行流程: Java 编译器(javac)将 Java 代码转换为字节码(.class 文件)

1. 类加载器(ClassLoader)

负责加载 .class 文件,将 Java 字节码加载到内存中,并交给 JVM 执行

2. 运行时数据区(Runtime Data Area)

管理JVM使用的内存。主要包括:

技巧区(Method Area):存储类的元数据、常量、静态变量等。

堆(Heap):存储所有对象和数组,垃圾回收器主要回收堆中的对象。

栈(Stack):每个线程都有一个栈,用于存储局部变量、 技巧调用等信息。

程序计数器(PC Register):每个线程有一个程序计数器,指示当前线程正在执行的字节码指令地址。

本地 技巧栈(Native Method Stack):支持本地 技巧的调用(通过 JNI)。 其中 技巧区和堆是线程共享的,虚拟机栈、本地 技巧栈和程序计数器是线程私有的。

3. 执行引擎(Execution Engine)

负责执行字节码,包含:

解释器:逐条解释执行字节码。

JIT 编译器:将热点代码编译为机器码, 进步执行效率。

垃圾回收器:回收堆中的不再使用的对象,释放内存。

4. 本地库接口(Native Method Library)

允许 Java 程序通过 java本地接口JNI(Java Native Inte ce)调用本地 技巧(如 C/C++ 编写的代码),与底层 体系或硬件交互。

1.2 何是程序计数器?

难易程度:☆☆☆        出现频率:☆☆☆☆

程序计数器:线程私有的,每个线程一份,内部保存字节码的行号。用于记录正在执行的字节码指令的地址。 每个线程都有自己的程序计数器,确保线程切换时能够继续执行未完成的任务。

1.3 你能给我详细的介绍Java堆吗?⭐️⭐️

难易程度:☆☆☆        出现频率:☆☆☆☆

Java堆是 JVM 中用于存储所有对象和数组的内存区域。线程共享的区域。当堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。

它被分为:

年轻代(存储新创建的对象),被划分为三部分: Eden区:大多数新对象的分配区域; S0 和 S1(两个 大致严格相同的Survivor区):Eden 空间经过 GC 后存活下来的对象会被移到其中一个 Survivor 区域; 老年代:在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到老年代区间。 永久代:JDK 7 及之前,JVM 的 技巧区(也称永久代),保存的类信息、静态变量、常量、编译后的代码; 元空间:JDK 8 及之后,永久代被 Metaspace(元空间)取代,移除了永久代,把数据存储到了本地内存的元空间中,且其 大致不再受 JVM 堆的限制,防止内存溢出。

1.4 何是虚拟机栈

难易程度:☆☆☆        出现频率:☆☆☆☆

Java Virtual chine Stacks (java 虚拟机栈)

每个线程在 JVM 中私有的一块内存区域,称为虚拟机栈,先进后出,用于存储 技巧的局部变量和 技巧调用信息;

每个栈由多个栈帧(frame)组成,当线程执行 技巧时,为该 技巧分配一个栈帧(Stack Frame);

每个线程只能有一个活动栈帧,对应着当前正在执行的那个 技巧;

垃圾回收是否涉及栈内存?

垃圾回收主要指就是堆内存, 栈内存中不会有垃圾回收的概念, 由于栈内存是由 JVM 自动管理的, 技巧执行完成时,栈帧弹栈,内存就会释放;

栈内存分配越大越好吗?

未必,默认的栈内存通常为1024k; 栈内存过大会导致线程数变少,例如,机器总内存为512m,目前能活动的线程数则为512个,如果把栈内存改为2048k,那么能活动的线程数减半;

技巧内的局部变量是否线程安全?

技巧内的局部变量 本身是线程安全的, 由于它们存储在每个线程独立的栈中,不会被其他线程共享。 但如果局部变量是引用类型, 并且该引用指向的对象逃离了 技巧 影响范围(例如被返回或传递到外部),则需要考虑该对象的线程安全性。 如果对象是可变的, 并且被多个线程访问,可能会引发线程安全 难题。

栈内存溢出情况(StackOverflowError)

栈帧过多导致栈内存溢出; 典型 难题:递归调用会在栈中创建新的栈帧,如果递归深度过大,可能会导致栈空间耗尽,从而抛出

栈帧过大导致栈内存溢出

堆栈的区别是 何?

栈内存用来存储局部变量和 技巧调用,但堆内存是用来存储Java对象和数组的。 堆会GC垃圾回收,而栈不会; 栈内存是线程私有的,而堆内存是线程共有的; 两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常 栈空间不足:java.lang.StackOverFlowError 堆空间不足:java.lang.OutOfMemoryError

1.5 运行时数据区中包含哪些区域? ⭐️⭐️⭐️⭐️⭐️

JVM 运行时数据区包括 技巧区、堆、栈、程序计数器和本地 技巧栈。

技巧区存储类的元数据、常量池、静态变量和 JIT 编译后的代码。 类的结构信息:每个类的信息,如类名、父类名、接口、 技巧、字段的名称和描述等。常量池:存储常量值,如字符串常量、类常量等。 静态变量:属于类的变量,而不是某个实例的变量。 JIT编译后的代码是指 Jvm在运行时将热点代码从字节码编译为本地机器代码。 技巧区在 JDK 7 之前被称为 “永久代”,从 JDK 8 开始,永久代被移除,改为使用元空间 堆是 JVM 中最大的内存区域,负责存储所有的对象实例和数组,并进行垃圾回收。 虚拟机栈存储每个线程的局部变量、 技巧调用信息和返回地址等。 程序计数器是每个线程私有的,用于存储当前线程正在执行的字节码指令的地址。 本地 技巧栈支持 JNI 本地 技巧调用,线程私有。 专门为本地 技巧调用而设计。它用于执行本地代码时所需的栈空间。

在 JDK 1.8 时 JVM 的内存结构主要有两点不同,

一个是 技巧区(Method Area)在 JDK 1.8 被替换为元空间(Metaspace),且元空间使用本地内存。 另一个是运行时常量池(Runtime Constant Pool)在 JDK 1.7 属于 技巧区的一部分,而在 JDK 1.8 变成元空间的一部分。

哪些线程私有?哪些线程共享?

线程私有的:线程计数器、虚拟机栈、本地 技巧栈 线程共享的:堆、 技巧区

哪些区域可能会出现 OutOfMemoryError?

堆(Heap): 当堆内存不足时,会抛出OutOfMemoryError。 技巧区(Method Area): 当 技巧区内存不足时,会抛出OutOfMemoryError,通常是 由于加载的类太多或常量池中的数据过多。

1.6 能不能解释一下 技巧区?

难易程度:☆☆☆        出现频率:☆☆☆

技巧区 是 JVM 运行时数据区的一部分,主要用于存储类的信息、常量、静态变量以及 JIT 编译后的代码。 在 JDK 7 之前,这部分内存称为永久代(PermGen),而在 JDK 8 以后,永久代被移除,取而代之的是元空间(Metaspace),它位于本地内存中,不再受堆内存限制。

虚拟机启动的时候创建,关闭虚拟机时释放。

技巧区的内存由 JVM 管理,并在类卸载时进行垃圾回收。

如果 技巧区域中的内存无法满足分配请求,则会抛出OutOfMemoryError: Metaspac。

技巧区和永久代以及元空间是 何关系呢? ⭐️

技巧区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是 技巧区, 技巧区是java虚拟机规范中的一个概念,而永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中 技巧区的两种实现方式。 永久代是 JDK 1.8 之前的 技巧区实现,JDK 1.8 及以后 技巧区的实现变成了元空间。

元空间和 技巧区的区别?

元空间(Metaspace)和 技巧区的主要区别在于:

内存存储位置: 技巧区在JVM的堆内存中,而元空间使用的是本地内存(Native Memory)。

内存管理: 技巧区受JVM管理,可能导致OutOfMemoryError;元空间由操作 体系管理,避免了这个 难题。

JVM版本差异: 技巧区在JDK 7及之前存在,JDK 8后被元空间取代。

内存溢出: 技巧区可能 由于空间不足引发OutOfMemoryError,而元空间则避免了这个 难题, 由于它不依赖堆内存。

元空间有 何 影响? 何故要有元空间?

元空间(Metaspace)的 影响是存储JVM类的元数据,它在JDK 8及之后的版本中取代了之前的永久代(PermGen)。 与永久代不同,元空间不再使用堆内存,而是直接使用本地内存(Native Memory)。这是为了 进步性能和避免永久代内存溢出的风险。

主要有两个 缘故:

解决内存溢出 难题:JDK 7 及之前的永久代(PermGen)存储类元数据,容易出现 OutOfMemoryError。而元空间(Metaspace)使用本地内存,仅受 体系内存限制,避免了该 难题。

性能优化:元空间使用本地内存而非堆内存存储类元数据,提升了类加载性能,减少了垃圾回收(GC)的压力, 由于元空间的回收由 JVM 和操作 体系共同管理,无需频繁触发 GC。

Java对象的创建 经过?⭐️⭐️⭐️⭐️⭐️

类加载检查,当程序执行到 new 指令时,JVM 会先检查对应的类是否已经被加载、解析和初始化过。如果类尚未加载,JVM 会按照类加载机制(加载、验证、准备、解析、初始化)完成类的加载 经过。这一步确保了类的元信息(如字段、 技巧等)已经准备好,为后续的对象创建奠定基础。 内存的分配,JVM 会为新对象分配内存空间。对象所需的内存 大致在类加载完成后就可以确定,因此分配内存的 经过就是从堆中划分一块连续的空间,主要有两种方式:

①一种是通过指针碰撞,如果堆中的内存是规整的(已使用和空闲区域之间有明确分界),JVM 可以通过移动指针来分配内存。 ②另一种是通过空闲列表,如果堆中的内存是碎片化的,JVM 会维护一个空闲列表,记录可用的内存块,并从中分配合适的区域。 除了这些之后,为了保证多线程环境下的安全性,JVM 还会采用两种策略避免内存分配冲突,一种是通过 CAS 操作尝试更新分配指针,如果失败则重试;另一种是每个线程在堆中预先分配一小块专属区域,避免线程间的竞争。

零值初始化,JVM 会对分配的内存空间进行初始化,将其所有字段设置为零值(如 int 为 0,boolean 为 false,引用类型为 null)。这一步确保了对象的实例字段在未显式赋值前有一个默认值,从而避免未初始化的变量被访问。 设置对象头,其中包含Mark Word、Klass Pointer和数组长度。Mark Word 用于存储对象的哈希码、GC 分代年龄、锁 情形标志等信息。Klass Pointer 指向对象所属类的元数据(即 Person.class 的地址)。 执行构造 技巧,用<init> 技巧完成对象的初始化。构造 技巧会根据代码逻辑对对象的字段进行赋值,并调用父类的构造 技巧完成继承链的初始化。这一步完成后,对象才真正可用。

Java 创建对象的四种常见方式

new 关键字(最常见)

使用 new 关键字可以直接创建对象,并调用 无参或有参构造 技巧 进行初始化。

反射

反射机制可以在运行时动态创建对象。

clone() 技巧(对象克隆)

clone() 技巧用于创建一个相同内容的新对象,不会调用构造 技巧。需要实现 Cloneable 接口,并重写 clone() 技巧。

反序列化(Serializable)

反序列化可以将存储或传输的对象数据恢复成 Java 对象,不会调用构造 技巧。

对象访问定位的两种方式知道吗?优缺点?⭐️

对象访问定位是 JVM 中通过引用变量找到实际对象的 经过。在 Java 中,有两种主要的对象访问定位方式:句柄和直接指针。

句柄访问:引用变量存储的是句柄的地址,句柄再指向对象。

优点:GC 时对象移动不影响引用,稳定性高;实例和类型数据分离,便于管理。 缺点:访问慢(两次跳转),内存占用大(需要句柄池)。

直接指针访问:引用变量直接存储对象地址。

优点:访问快(一次跳转),内存占用小。 缺点:GC 时需要修改所有引用,开销大。

1.7 介绍一下运行时常量池?

常量池

可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、 技巧名、参数类型、字面量等信息

运行时常量池

常量池是 .class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为 诚恳地址

1.8 你听过直接内存吗?

难易程度:☆☆☆        出现频率:☆☆☆

它不受 JVM 内存回收管理,是虚拟机的 体系内存; 常见于在 NIO 中使用直接内存,不需要在堆中开辟空间进行数据的拷贝,jvm可以直接操作直接内存,从而使数据读写传输更快。但分配回收成本较高。

使用传统的IO的 时刻要比NIO操作的 时刻长了很多,也就说NIO的读性能更好。

这个是跟我们的JVM的直接内存是有一定关系,如下图,传统阻塞IO的数据传输流程和NIO传输数据的流程。

2. 类加载器

2.1 何是类加载器,类加载器有哪些?

难易程度:☆☆☆☆        出现频率:☆☆☆

JVM只会运行二进制文件,而类加载器(ClassLoader)的主要 影响就是将.class字节码文件加载到JVM内存,生成对应的Class对象,供程序使用。

启动类加载器(BootStrap ClassLoader): 该类并不继承ClassLoader类,其是由C++编写实现。用于加载J A_HOME/jre/lib目录下的类库。

扩展类加载器(ExtClassLoader): 该类是ClassLoader的子类,主要加载J A_HOME/jre/lib/ext目录中的类库。

应用类加载器(AppClassLoader): 该类是ClassLoader的子类,主要用于加载classPath下的类,也就是加载开发者自己编写的Java类。

自定义类加载器: 开发者自定义类继承ClassLoader,实现自定义类加载 制度。

类加载器的体系并不是“继承”体系,而是委派体系,类加载器首先会到自己的parent中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次

2.2  何是双亲委派模型?⭐️⭐️⭐️⭐️

难易程度:☆☆☆☆        出现频率:☆☆☆☆

双亲委派模型要求类加载器在加载某一个类时,先委托父加载器尝试加载。 如果父加载器可以完成类加载任务,就返回成功; 只有父加载器无法加载时,子加载器才会加载。

2.3 JVM 何故采用双亲委派机制?(好处)⭐️

避免类的重复加载:父加载器加载的类,子加载器无需重复加载。

保证核心类库的安全性:为了安全,保证类库API不会被修改。如 java.lang包下的类只能由 启动类加载器Bootstrap ClassLoader 加载,防止被篡改。

2.4 说一下类的 生活周期?

一个类从被加载到虚拟机内存中开始,到从内存中卸载,它的整个 生活周期包括了: 加载、验证、准备、解析、初始化、使用和卸载这7个阶段。 其中,验证、准备和解析这三个部分统称为连接(linking)。

2.5 说一下类装载的执行 经过?⭐️⭐️⭐️⭐️⭐️

难易程度:☆☆☆☆☆        出现频率:☆☆☆

类装载 经过包括三个阶段:载入、连接和初始化,连接细分为 验证、准备、解析,这是标准的 JVM 类装载流程。

加载(Loading):通过类加载器找到 .class 文件读取到内存,生成 Class 对象。 连接(Linking): 验证:检查字节码是否合法,防止恶意代码破坏 JVM; 准备:为类的静态变量分配内存并设置默认初始值,但不执行赋值逻辑; 解析:将常量池中的 符号引用(如类名、 技巧名)转为 直接引用(内存地址)。 初始化(Initialization):执行类的静态代码块和静态变量赋值。

在准备阶段,静态变量已经被赋过默认初始值了,在初始化阶段,静态变量将被赋值为代码期望赋的值。比如说 static int a = 1;,在准备阶段,a 的值为 0,在初始化阶段,a 的值为 1

类装载完成后的阶段:加载完成后,类进入‘使用阶段’。当 Class 对象不再被引用时,可能触发‘卸载’。

使用:JVM 通过 Class 对象创建实例、调用 技巧,进入正常运行阶段。 卸载:当 Class 对象不再被引用时,由 GC 回收,但 JVM 核心类(如 java.lang.*)不会被卸载。

3. 垃圾回收

3.1 简述Java垃圾回收机制?(GC是 何? 何故要GC)

GC(Garbage Collection,垃圾回收)是 Java 中自动管理内存的机制,负责回收不再使用的对象,以释放内存空间。 垃圾回收是 Java 程序员不需要显式管理内存的一大优势,它由 JVM 自动进行。

GC 的主要目的是: 自动管理内存:程序运行 经过中会创建大量的对象,但一些对象在使用完后不再被引用。手动管理这些对象的内存释放非常繁琐且容易出错; 防止内存泄漏:如果不及时释放无用对象的内存, 体系的可用内存会越来越少,最终可能导致 OutOfMemoryError; 避免内存溢出:GC 机制能够保证内存不会 由于长期积累未回收的对象而耗尽。

3.2 对象 何时候候可以被垃圾器回收? 怎样判断对象是否死亡?⭐️

难易程度:☆☆☆☆        出现频率:☆☆☆☆

如果一个或多个对象没有任何的引用指向它了,那么这个对象现在就是垃圾,如果定位了垃圾,则有可能会被垃圾回收器回收。

如果要定位 何是垃圾,有两种方式来确定, 1. 引用计数法:通过计数引用的数量,当引用为 0 时回收。但不能处理循环引用 这种 技巧通过给每个对象维护一个引用计数器。当有一个新的引用指向该对象时,引用计数加 1;当引用离开时,计数减 1。 2. 可达性分析算法:通过检查对象是否从根对象可达,无法访问的对象可以回收。是 Java 使用的主要 技巧。

根对象是那些肯定不能当做垃圾回收的对象,就可以当做根对象 根对象包含:虚拟机栈中引用的对象;静态变量;活动线程的引用;JNI 引用的对象。

3.3 JVM 垃圾回收算法有哪些?⭐️⭐️⭐️⭐️⭐️

难易程度:☆☆☆        出现频率:☆☆☆☆

Java 中的垃圾回收器采用不同的算法来回收不再使用的对象。

标记-清除算法:简单,效率高,但有内存碎片; 从根对象开始,遍历所有可以访问到的对象,并标记它们。 遍历所有对象,清除那些未被标记的对象,即不可达的对象。 标记-整理算法:解决了碎片 难题,但整理 经过较慢,效率低; 与标记-清除算法相同,标记所有可达的对象。 清除不可达对象后,将存活的对象移动到内存的一端,消除内存碎片。 算法:无碎片,内存整理高效,但内存利用率低; 将堆内存分为两部分,每次只使用其中一部分。当一部分用完时,垃圾回收器将存活的对象 到另一部分,并清空当前使用的部分。 分代收集算法

3.4 分代收集算法

分代收集算法-堆的区域划分

java8时,堆被分为了两份:新生代和老年代【1:2】,在java7时,还存在一个永久

对于新生代,内部又被分为了三个区域。 伊甸园 Eden 区,幸存者区 survivor (分成 from 和 to )【8:1:1】

分代收集算法- 职业机制

新创建的对象,都会先分配到eden区 当伊甸园内存不足,标记伊甸园与 from(现阶段没有)的存活对象 存活对象采用 算法 到 to 中, 完后,伊甸园和 from 内存都得到释放(即清空

经过一段 时刻后伊甸园的内存又出现不足,标记eden区域to区存活的对象,将存活的对象 到from区

又来了一批数据

当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足或大对象会导致提前晋升)

何故要用垃圾分代回收?

分代收集的主要目的是 进步垃圾回收效率,减少GC的停顿 时刻。其基本 想法是将堆内存分成多个区域(通常是年轻代、老年代和持久代),根据对象的存活 时刻来进行不同的回收策略。

年轻代(Young Generation):存放刚创建的对象。 由于大部分对象都是短命的,年轻代回收主要采用 算法,回收效率高,能够快速回收短 生活周期的对象。

老年代(Old Generation):存放长 时刻存活的对象。老年代对象较少变化,回收时使用标记-清除或标记-整理算法,回收速度相对较慢,但减少了频繁的垃圾回收。

持久代(Per nent Generation)(JDK 7及之前)/元空间(Metaspace)(JDK 8及之后):存放类信息等元数据。虽然它不涉及对象的存活与否,但也通过分代管理避免频繁回收。

分代收集的优势:

高效回收:通过将对象按存活 时刻分代,年轻代的回收可以频繁进行,而老年代则较少回收,这样可以显著 进步回收效率。

减少停顿 时刻:年轻代采用 算法,效率高且停顿 时刻短,减少了GC对应用程序性能的影响。

MinorGC、 Mixed GC 、 FullGC的区别是 何?

MinorGC(年轻代垃圾回收) 只回收年轻代,频繁且代价较低,停顿 时刻短。 对象会在年轻代中被回收,短命对象被清理,存活的对象晋升到老年代。 由于年轻代内存较小,回收的代价通常较低,停顿 时刻短,回收效率高。

Mixed GC(混合垃圾回收) 回收年轻代 + 部分老年代,G1 回收器特有,减少了老年代Full GC的频率,停顿 时刻比Minor GC长,但比Full GC短。 Mixed GC会回收年轻代的对象,同时也回收老年代的一部分。具体回收哪些老年代区域,由JVM根据内存压力和区域分布决定。 这种回收方式能减少老年代的Full GC触发次数。 Mixed GC比Minor GC涉及的内存范围大,因此会导致更长的停顿 时刻。相比Full GC,它的停顿 时刻仍然较短,但比Minor GC长。

FullGC(完全垃圾回收) 回收整个堆(年轻代、老年代、永久代/元空间),停顿 时刻最长(STW),对性能影响最大,应该尽量避免。 Full GC会检查整个堆内存,进行垃圾回收。它不仅回收年轻代,还回收老年代和永久代(或元空间)。这 一个全局性的回收 经过,因此会占用更多的 时刻,导致较长的停顿

STW(Stop-The-World):暂停所有应用程序线程,等待垃圾回收的完成

3.5 说下 JVM 有哪些垃圾回收器?⭐️⭐️⭐️⭐️

难易程度:☆☆☆☆        出现频率:☆☆☆☆

在jvm中,实现了多种垃圾收集器,包括:

串行垃圾收集器

并行垃圾收集器

CMS(并发)垃圾收集器

G1垃圾收集器

串行垃圾收集器

Serial和Serial Old串行垃圾收集器,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑

Serial 影响于新生代,采用 算法

Serial Old 影响于老年代,采用标记-整理算法

垃圾回收时,只有一个线程在 职业, 并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成。

并行垃圾收集器

Parallel New和Parallel Old 一个并行垃圾回收器,JDK8默认使用此垃圾回收器

Parallel New 影响于新生代,采用 算法

Parallel Old 影响于老年代,采用标记-整理算法

垃圾回收时,多个线程在 职业, 并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成。

CMS(并发)垃圾收集器

CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器, 该回收器是针对老年代垃圾回收的,是一款以获取最短回收停顿 时刻为目标的收集器, 停顿 时刻短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行。

3.6 详细聊一下G1垃圾回收器

难易程度:☆☆☆☆        出现频率:☆☆☆☆

G1 在 JDK 1.7 时引入,在 JDK 9 时取代 CMS 成为默认的垃圾收集器。 G1 把 Java 堆划分为多个 大致相等的独立区域Region,每个区域都可以扮演新生代或老年代的角色。 同时,G1 还有一个专门为大对象设计的 Region,叫 Humongous 区。 这种区域化管理使得 G1 可以更灵活地进行垃圾收集,只回收部分区域而不是整个新生代或老年代。 G1 GC的设计目标是能够在大内存环境中提供高吞吐量,同时尽量减少垃圾回收的停顿 时刻。

如果并发失败(即回收速度赶不上创建新对象速度),会触发 Full GC。

大对象的判定 制度是,如果一个大对象超过了一个 Region 大致的 50%,比如每个 Region 是 2M,只要一个对象超过了 1M,就会被放入 Humongous 中。

G1 收集器的运行 经过大致可划分为这 几许步骤:

初始标记: 这一步会触发一次短暂停顿(Stop-The-World,STW),标记从 GC Roots 可以直接引用的对象,即标记所有直接可达的活跃对象。 并发标记,这一阶段与应用并发运行,标记堆中所有可达的对象。 这个阶段可能会持续较长 时刻,具体 时刻取决于堆的 大致和对象数量。 混合收集,在并发标记完成后,G1 会计算出哪些区域的回收 价格最高,即哪些区域包含最多垃圾。 接着,它优先回收这些区域,包括部分年轻代区域和老年代区域。 通过选择回收成本低而收益高的区域,G1 进步了回收效率,并尽量减少了停顿 时刻。 可预测的停顿,在垃圾回收期间,G1仍然需要进行停顿,但它提供了预测机制。用户可以通过JVM启动参数指定期望的最大停顿 时刻,G1会尽量在此 时刻内完成垃圾回收,以确保应用性能。

3.7 JDK中有几种引用类型?分别的特点是 何?⭐️

在Java中,引用类型有不同的级别,它们控制着对象的 生活周期以及垃圾回收的行为。强引用、软引用、弱引用和虚引用都是Java中引用对象的方式,它们的区别主要体现在垃圾回收器回收对象的时机和条件上。

强引用(Strong Reference)

最常见的引用类型,只要强引用存在,对象就不会被垃圾回收。 只有当引用被显式置为null或者没有任何引用指向该对象时,垃圾回收器才会回收它 Object obj = new Object(); // 强引用 obj = null; // 显式置为 null,对象才可能被 GC 若强引用对象过多,可能导致内存泄漏或 OOM。

软引用(Soft Reference)

软引用用于描述那些在内存充足时不应回收、但在内存不足时可以回收的对象。 需要配合SoftReference使用 SoftReference<Object> softRef = new SoftReference<>(new Object());

弱引用(Weak Reference)

弱引用比软引用更弱,每次垃圾回收时都会被回收,无论内存是否充足。 需要配合WeakReference使用 WeakReference<Object> weakRef = new WeakReference<>(new Object());

虚引用(Phantom Reference)

虚引用是最弱的引用类型,必须与引用队列(ReferenceQueue)配合使用。 虚引用的对象被回收时,会将虚引用加入引用队列,由Reference Handler线程调用虚引用相关 技巧来释放直接内存。 虚引用的主要 影响是用来监控对象被回收的 情形。

4. JVM 操作(调优)

4.1 JVM 调优的参数可以在 何处设置参数值?

难易程度:☆☆        出现频率:☆☆☆

我们当时的项目是springboot项目,可以在项目启动的时候,java -jar中加入参数就行了

tomcat的设置vm参数

war包部署在tomcat中设置 修改TOMCAT_HOME/bin/catalina.sh文件 J A_OPTS=”-Xms512m -Xmx1024m”

linux下是.sh 小编觉得,windows是.bat 小编觉得

springboot项目jar文件启动

jar包部署在启动参数设置 通常在linux 体系下直接加参数启动springboot项目 java -Xms512m -Xmx1024m

nohup java -Xms512m -Xmx1024m -jar xxxx.jar –spring.profiles.active=prod & nohup : 用于在 体系后台不挂断地运行命令,退出终端不会影响程序的运行 参数 & :让命令在后台执行,终端退出后命令仍旧执行。

4.2 用的 JVM 调优的参数都有哪些?

难易程度:☆☆☆        出现频率:☆☆☆☆

嗯,这些参数是比较多的。我记得当时我们设置过:…。具体的指令记不太清楚。

设置堆内存 大致 -Xms:初始堆 大致 -Xmx:最大堆 大致 设置年轻代中Eden区和两个Survivor区的 大致比例 -XX:NewSize=n:设置年轻代 大致 -XX:NewRatio=n:设置年轻代和年老代的比值。如:n 为 3 表示年轻代和年老代比值为 1:3,年轻代占总和的 1/4 -XX:SurvivorRatio=n:年轻代中 Eden 区与两个 Survivor 区的比值。如 n=3 表示 Eden 占 3, Survivor 占 2,一个 Survivor 区占整个年轻代的 1/5 设置使用哪种垃圾回收器 -XX:+UseSerialGC:设置串行收集器 -XX:+UseParallelGC:设置并行收集器 -XX:+UseParalledlOldGC:设置并行老年代收集器 -XX:+UseConcMarkSweepGC:设置并发收集器 年轻代晋升老年代阈值 虚拟机栈的设置

4.3 说一下 JVM 调优的工具?

难易程度:☆☆☆☆        出现频率:☆☆☆☆

嗯,我们一般都是使用jdk自带的一些工具,比如

命令工具

jps

输出JVM中运行的进程 情形信息。 jps

jstack

查看java进程内线程的堆栈信息。 jstack [option] <pid> 

j p

用于生成堆转存快照

j p [options] pid 内存映像信息

j p -heap pid 显示Java堆的信息

j p -dump:for t=b,file=heap.hprof pid

for t=b表示以hprof二进制格式转储Java堆的内存

file=<filename>用于指定快照dump文件的文件名。

jhat

用于分析j p生成的堆转存快照(一般不推荐使用,而是使用Ecplise Memory Analyzer)

jstat

是JVM统计监测工具。可以用来显示垃圾回收信息、类加载信息、新生代统计信息等。

拓展资料垃圾回收统计:jstat -gcutil pid

可视化工具

jconsole

JDK 自带的监控工具, 用于对jvm的内存,线程,类 的监控 打开方式:java 安装目录 bin目录下 直接启动 jconsole.exe 就行

VisualVM:故障处理工具

能够监控线程,内存情况,查看 技巧的CPU 时刻和内存中的对 象,已被GC的对象,反向查看分配的堆栈 打开方式:java 安装目录 bin目录下 直接启动 jvisualvm.exe就行

4.4 java内存泄露的排查思路?

难易程度:☆☆☆☆        出现频率:☆☆☆☆

内存泄漏通常是指堆内存,通常是指一些大对象不被回收的情况。

通过j p或设置jvm参数获取堆内存快照dump 通过工具,VisualVM去分析dump文件,VisualVM可以加载离线的dump文件 通过查看堆信息的情况,可以大概定位内存溢出是哪行代码出了 难题 找到对应的代码,通过阅读上下文的情况,进行修复即可

4.5 CPU飙高排查方案与思路?

难易程度:☆☆☆☆        出现频率:☆☆☆☆

使用top命令查看占用cpu的情况 通过top命令查看后,可以查看是哪一个进程占用cpu较高,记录这个进程id 使用ps命令查看进程中的线程信息,看看 何者线程的cpu占用较高 使用jstack命令查看进程中哪些线程出现了 难题,最终定位 难题代码的行号