- 1. 类加载
- 1.1 类的生命周期说一下?
- 1.2 介绍下生命周期中的加载?
- 1.3 介绍下生命周期中的验证?
- 1.4 介绍下生命周期中的准备?
- 1.5 介绍下生命周期中的解析?
- 1.6 介绍下生命周期中的初始化?
- 1.7 介绍下生命周期中的卸载?
- 1.8 介绍下类加载器?
- 1.9 JVM中都有哪些类加载器?
- 1.10 怎么实现自定义类加载器?
- 1.11 介绍下双亲委派模型?
- 1.12 怎么屏蔽双亲委派模型?
- 2. JVM内存管理——重点
- 2.1 介绍一下Java运行时内存模型?【JVM内存是怎么划分的?】
- 2.2 Java运行内存模型中,哪些是线程共享的,哪些是私有的?
- 2.3 介绍一下方法区?
- 2.4 介绍一下堆内存?
- 2.5 介绍一下虚拟机栈?
- 2.6 介绍一下本地方法栈?
- 2.7 介绍一下程序计数器?
- 2.8 Java内存是怎么给对象分配内存的?
- 2.9 新生代和老年代介绍下?
- 2.10 新生代内部模型介绍下?
- 2.11 为什么要设计两个幸存区?
- 3. JVM垃圾回收——重点
- 3.1 都有哪些垃圾回收器?
- 3.2 CMS和G1有什么区别?
- 3.3 GC垃圾回收介绍下?
- 3.4 垃圾搜索算法介绍下?【怎么判断对象是否可以被回收?】
- 3.5 引用计数法的缺点是什么?
- 3.6 Root根搜索遍历法的缺点是什么?
- 3.7 垃圾回收算法介绍下?每种算法的优缺点是什么?
- 3.8 GC日志有看过吗?都有哪些信息
- 4. JVM参数
- 4.1 JVM常用参数有哪些?
- 4.2 JVM堆内存相关参数有哪些?
- 4.3 如何设置堆内存新生代大小?
- 4.4 如何设置堆内存老年代大小?
- 4.5 如何设置元空间/方法区大小?
- 4.6 JVM堆内存一般怎么设置大小?
- 4.7 如何设置垃圾回收算法?
- 4.8 如何设置伊甸区和幸存区的比例?
- 5. JVM性能与调优
- 5.1 JVM常用命令有哪些?
- 5.2 怎么利用JVM命令分析死锁?
- 5.3 怎么导出堆内存信息?
- 5.4 怎么查看JVM线程信息?
- 5.5 JVM提供的可视化工具有哪些?
1. 类加载
1.1 类的生命周期说一下?
加载、验证、准备、解析、初始化、使用、卸载
1.2 介绍下生命周期中的加载?
通过类的全限定名获取类文件的二进制信息,然后将方法区二进制流信息加载到方法区,并且生成一个class入口
1.3 介绍下生命周期中的验证?
验证主要是验证class文件是否符合《JVM规范》,主要由四个阶段组成:
- 阶段一:文件格式验证,验证class文件格式
- 阶段二:元数据验证,检查class文件的语义
- 阶段三:字节码验证,检查程序语义
- 阶段四:符号验证,检查程序的引用符号是否正确
1.4 介绍下生命周期中的准备?
主要是给类的变量设置初始化数据,这个数据不等于程序中的初始化值,而是JVM的默认值
1.5 介绍下生命周期中的解析?
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。
1.6 介绍下生命周期中的初始化?
类的初始化是一个重要的阶段,它涉及到类实例(对象)在内存中分配空间后,立即进行的一系列设置和准备工作,以确保对象能够正确地按照类的定义进行后续的操作。
类初始化相关工作包括:内存对象分配,执行构造方法,静态变量赋值等
1.7 介绍下生命周期中的卸载?
类卸载就是将方法区里的Class对象进行GC回收
1.8 介绍下类加载器?
类加载器(Class Loader)是Java虚拟机(JVM)的一个重要组成部分,它负责将Java类的二进制代码加载到内存中,并转换为可执行的Java字节码。类加载器的作用如下:
- 加载类:将类加载到JVM中
- 转换为字节码:将
.Class
文件转化为字节码 - 管理类命名空间:保证类的全限定类名唯一,保证每个类的隔离性
1.9 JVM中都有哪些类加载器?
除了自定义类加载器外,JVM有三种默认的类加载器:
- 启动类加载器:主要是加载Java类的核心库,是jdk环境下的
Lib
包内容,例如:Java.lang.*
等 - 扩展类加载器:主要是加载Java类的扩展库,是JDK环境下的
ext
包内容 - 应用程序类加载器:是扩展类加载器的子类,主要是加载用户路径下的类
1.10 怎么实现自定义类加载器?
继承ClassLoader
抽象类,实现里面的loadClass()
方法和findClass()
即可
1.11 介绍下双亲委派模型?
类加载器在加载类的时候,首先是调用类的父类,尝试用父类加载,如果父类加载失败,再调用类加载器加载。
1.12 怎么屏蔽双亲委派模型?
自定义类里面,只需要重写loadClass()
,将尝试父类加载的代码部分屏蔽即可。
2. JVM内存管理——重点
先明白JVM内存是怎么分布的:
2.1 介绍一下Java运行时内存模型?【JVM内存是怎么划分的?】
先说内存模块,再简单说每个模块的功能就OK了
Java内存模型可以分为五个部分,线程共有的包括堆内存 和 方法区,或者也可以叫元空间;线程私有的包括 虚拟机栈、本地方法栈 和 程序计数器。其中每个部分功能如下:
- 堆内存:主要存储Java对象实例信息
- 方法区:主要是存储静态信息和Class类实例信息
- 本地方法栈:主要是存储本地方法调用指针
- 虚拟机栈:主要是存储基础数据信息和对象引用指针
- 程序计数器:主要是记录线程内部程序的执行位置
2.2 Java运行内存模型中,哪些是线程共享的,哪些是私有的?
- 共享的:方法区和堆内存
- 私有的:程序计数器、虚拟机栈、本地方法栈
2.3 介绍一下方法区?
方法区在jdk8后也叫元空间,方法区是线程共享的,主要存储的是静态信息和类信息,主要包括常量、静态变量和Class类实例信息。Class实例信息就是Java文件中声明的Java类。
方法区在不同版本,使用的内存区域也不同,在jdk8之前,用的JVM内部分配的空间;jdk8及以后,用的都是直接内存。
方法区一般分配的内存比较小,大概50M之内就可以满足运行需求,而且方法区的空间一般不会被GC回收。
2.4 介绍一下堆内存?
堆内存是线程共享的,也是GC和对象实例内存分配的主要区域,堆内存包括两个部分,分别是新生代和老年代,新生代和老年代的大小可以通过JVM参数配置。新生代中划分三个区域,分别是两个幸存区和一个伊甸区,新创建的对象首先是分配在伊甸区的。
2.5 介绍一下虚拟机栈?
虚拟机栈是线程私有的,主要是保存对象的引用指针和基础数据值,比如int,long这些基础数据类型值就是保存在虚拟机栈中的。引用指针里面存储的是堆内存中对象地址,可以通过引用指针直接访问堆内存对象数据。
2.6 介绍一下本地方法栈?
本地方法栈是线程私有的,主要是存储本地方法的调度指针、调度参数和环境变量等信息
2.7 介绍一下程序计数器?
程序计数器是线程私有的,主要是记录程序的执行节奏,跟踪程序执行过程,会将程序的执行指令地址存储到程序计数器中。
2.8 Java内存是怎么给对象分配内存的?
一个对象通过构造方法创建后,首先判断对象的大小,如果超过16K,则将对象直接放入到老年代。否则,先将对象放入到伊甸区。在经过Minor GC
后,如果对象没有被回收,则将对象放入到幸存区。如果经过多次回收后,确定该对象为长期存活对象,则将该对象放入到老年代中。
默认
Minor GC
回收15次之后,对象进入老年代。
不过回收次数可以配置,通过-XX:MaxTenuringThreshold
可以设置回收多少次后,进入老年代。
2.9 新生代和老年代介绍下?
新生代和老年代都是Java内存的一种管理方式,在Java内存模型中,堆内存里面被划分为两个区域,即新生代区和老年代区。
- 新生代区主要是保存一些程序新建对象,这些对象的特点是朝生夕死,也就是说这些对象一般会频繁地创建和销毁。而且,JVM GC的90%都是在新生代发生。
- 老年代区主要是保存一些常驻对象,这些对象的特点是生命存活时间比较长,或者对象比较大。
2.10 新生代内部模型介绍下?
新生代里面有一个伊甸区和两个幸存区,默认比例是8:1:1
。
2.11 为什么要设计两个幸存区?
设计两个幸存区主要是基于内存管理和GC效率两方面考虑的,因为对于新生代垃圾回收算法现在主流采用的是标记-复制算法,通过两个幸存区可以提高GC回收效率,减少内存碎片。
3. JVM垃圾回收——重点
3.1 都有哪些垃圾回收器?
垃圾回收器主要分为三类,分别是:
- 串行回收器:
serial
,serial old
- 并行回收器:
Parallel
,ParNew
- 并发回收器:
CMS
和G1
3.2 CMS和G1有什么区别?
CMS和G1都是Jvm的垃圾回收器,两者的区别主要有以下几个方面:
- 收集范围:CMS只会回收老年代,G1则是混合回收器,老年代和新生代都会回收
- 收集算法:CMS用的是标记-清除算法, G1则是
标记-整理
算法 - 停顿时间:CMS尽量使停顿时间最短来满足用户需求,G1则是根据回收价值大小,在停顿时间范围内尽量回收价值大的内存区域
- 并发与吞吐量:CMS在并发阶段,CMS会与应用程序线程并发执行,以减少对应用程序性能的影响。但是,CMS对CPU资源非常敏感,在并发阶段会占用CPU资源而导致应用程序变慢,总吞吐量下降。G1充分利用多核处理器和大内存容量的优势,使用多个CPU来缩短停顿时间并提高吞吐量。G1的并发能力更强,能够更好地适应高负载的应用场景。
- 浮动垃圾处理:CMS本次产生的浮动垃圾需要下次GC才会清理,G1则是尽可能在本次GC就处理
3.3 GC垃圾回收介绍下?
GC是JVM提供的垃圾管理工具,主要用于自动管理程序中不再使用的内存,避免程序员手动释放内存,自动检测回收垃圾,避免内存泄漏风险。
3.4 垃圾搜索算法介绍下?【怎么判断对象是否可以被回收?】
垃圾回收算法包括两种,即:
- root根搜索遍历法:随机挑选几个对象,然后找该对象的关联的对象,然后广度搜索再找关联对象的关联对象,并且会将这个对象所有直接或者间接关联的对象组成一个图,对于图内的对象不会回收。对于不在图内的对象,逐个进行图关联,如果存在单节点图,则这些节点会被回收。
- 引用计数法:相当于给对象增加一个计数器,每有一个指针引用该对象,则给计数器加一。每次回收前,直接判断计数器是否等于0,如果等于则直接回收。
3.5 引用计数法的缺点是什么?
缺点主要是根据算法特性来决定,主要的缺点包括如下三个方面:
- 额外的存储开销和性能开销:因为要维护一个计数器,并且要频繁地更新计数器,所以会有存储和性能开销。
- 无法解决循环引用问题:比如A引用B,B引用C,C引用A,那么对于ABC三个对象来说,计数器一直都是1,无法满足回收条件。
- 边界兼容性问题不好:对象是类的具体实例,但是对于类来说,存在继承、实现等多种兼容情况,这些情况在不同语言,或者环境下,都有边界问题。
3.6 Root根搜索遍历法的缺点是什么?
缺点也是相对于引用计数法来说的,并没有明确的缺点。可能会有如下缺点:
- 性能开销较高,因为组装树结构和广度遍历
- 结构复杂,难实现
- 有一定的停顿时间,影响GC效率
3.7 垃圾回收算法介绍下?每种算法的优缺点是什么?
目前主流的垃圾回收算法有三种,分别是:
- 标记-清除:两阶段,在垃圾搜索阶段,搜索到垃圾后,先对其进行标记。在垃圾回收阶段,会根据标记,进行垃圾清理。
- 优点:效率高,速率快,简单易实现,比较灵活
- 缺点:会产生内存碎片,导致内存利用率下降
- 复制:内存会划分为两块区域,每次分配对象只会在一块区域中分配。回收时,会将正在使用的对象复制到另一块内存区域,当前内存区域则会全部清理。例如:内存划分为两块,分别为A和B。每次分配对象内存时,只能给A和B中的一块分配,假如先给A分配,在垃圾回收阶段,会将A中正在使用的对象复制到B中,然后将A清理掉。下次回收时,反过来就行。
- 标记-整理:是标记-清楚和复制算法混合体,分为两阶段。第一阶段,将正在使用的对象进行标记。第二阶段,将标记的对象复制到内存的一端,剩下的部分被清除。
3.8 GC日志有看过吗?都有哪些信息
都包含如下关键信息:
- 触发时间戳
- 回收类型,例如
minor GC
,full GC
- 使用的垃圾回收器,例如
CMS
,G1
- 内存使用情况
- GC 耗时
- 堆信息
4. JVM参数
4.1 JVM常用参数有哪些?
常用参数包括启动参数和运行时参数两种,启动参数例如-classpath
等;运行时参数包括内存参数、GC相关参数等
4.2 JVM堆内存相关参数有哪些?
- 堆内存相关参数:
- -Xms:设置Java堆内存的初始大小。JVM启动时,会向操作系统请求此大小的内存。默认值取决于平台(通常是物理内存的1/64,但小于1GB)。
- -Xmx:设置Java堆内存的最大值。JVM可以扩展其堆内存,但不会超过此限制。默认值也取决于平台(通常是物理内存的1/4,但小于1GB)。
- 新生代设置:
- -Xmn:设置新生代(Young Generation)的内存大小。新生代是堆内存的一部分,用于存放新生成的对象。如果不设置此参数,新生代的大小将由JVM根据-Xms和-Xmx参数自动计算得出。
- -XX:NewSize:设置新生代的初始大小(在JDK 1.3/1.4中可用)。这个参数在较新版本的JDK中可能不再被直接使用,因为-Xmn已经包含了初始大小和最大大小的设置。
- -XX:MaxNewSize:设置新生代可以扩展到的最大大小(在JDK 1.3/1.4中可用)。同样,这个参数在较新版本的JDK中可能不再被推荐使用。
- 新生代分区比例设置:
- -XX:SurvivorRatio:设置Eden区与Survivor区的比例。Eden区是新生代中用于存放新生成对象的区域,而Survivor区则用于存放经过一次GC后仍然存活的对象。默认值为8,表示Eden区与单个Survivor区的比例为8:1。
- -XX:NewRatio:设置新生代与老年代(Old Generation)的比例。例如,如果设置为3,则表示新生代与老年代的比例为1:3。但请注意,当使用-Xmn或同时设置-Xms和-Xmx时,此参数可能不会被直接使用。
- -Xss:设置每个线程的栈内存大小。栈内存用于存放局部变量和方法调用的上下文信息。
4.3 如何设置堆内存新生代大小?
JVM -Xmn
可以设置新生代内存大小,也可以通过 -XX:NewRatio
设置新生代和老年代分区比例设置新生代大小。
4.4 如何设置堆内存老年代大小?
JVM并没有提供直接设置堆内存老年大大小的参数,但是可以通过别的方法解决,比如:
- 先设置堆内存整体大小,可以利用
xmx
和xms
设置堆内存最大值和最小值 - 然后设置新生代大小,两种方法:
- 一种是设置比例,利用
newRatio
设置新生代和老年代比例,这样可以通过新生代比例直接确定老年代大小 - 另一种直接设置新生代大小,利用
newSize
和maxNewSize
参数设置新生代大小最小最大值,然后通过总内存减去新生代大小,就可以得到老年代大小。
- 一种是设置比例,利用
4.5 如何设置元空间/方法区大小?
通过 JVM 的 MetaspaceSize
和 MaxMetaspaceSize
可以直接设置元空间的初始化大小和最大大小。
4.6 JVM堆内存一般怎么设置大小?
-xmx
: 设置堆内存的最大大小-xms
: 设置堆内存的最小值
4.7 如何设置垃圾回收算法?
垃圾回收算法直接和垃圾回收器是绑定的,JVM虽然没有直接设置回收算法的参数,但是可以设置垃圾会回收器来确定使用那种回收算法。
-XX:+UseSerialGC
:启用串行垃圾收集器(Serial GC)。这是一种单线程的收集器,适用于单核处理器或小型应用。-XX:+UseParallelGC
:启用并行垃圾收集器(Parallel GC)。这是一种多线程的收集器,适用于多核处理器。-XX:+UseConcMarkSweepGC
:启用CMS(Concurrent Mark Sweep)垃圾收集器。这是一种以尽可能减少停顿时间为目标的并发收集器。-XX:+UseG1GC
:启用G1(Garbage-First)垃圾收集器。G1收集器是一个面向服务器的垃圾收集器,旨在满足垃圾收集停顿时间要求的同时,还具备高吞吐量性能特征。
4.8 如何设置伊甸区和幸存区的比例?
通过JVM -**XX:SurvivorRatio**
来设置伊甸区和幸存区比例。
5. JVM性能与调优
5.1 JVM常用命令有哪些?
**jps**
(JVM Process Status): 类似 UNIX 的ps
命令。用于查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息;**jstat**
(JVM Statistics Monitoring Tool): 用于收集 HotSpot 虚拟机各方面的运行数据;**jinfo**
(Configuration Info for Java) : Configuration Info for Java,显示虚拟机配置信息;**jmap**
(Memory Map for Java) : 生成堆转储快照,此外可以用jmap -dump xx
导出内存快照。**jhat**
(JVM Heap Dump Browser) : 用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果。JDK9 移除了 jhat;**jstack**
(Stack Trace for Java) : 生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。
5.2 怎么利用JVM命令分析死锁?
利用jstack
可以查询当前JVM线程快照,在线程快照中查询死锁信息。一般如果有死锁的话,线程后面会提示Found one Java-level deadlock
提示信息。
5.3 怎么导出堆内存信息?
可以利用 jmap -dump
导出堆内存快照信息
5.4 怎么查看JVM线程信息?
利用jstack
查询线程信息
5.5 JVM提供的可视化工具有哪些?
- Jconsole:是一款基于JMX(Java Management Extensions)的可视化监视、管理工具。它随JDK一起发布,用于查看Java应用程序的运行概况、监控堆信息、永久区(元空间)使用情况、类加载情况等。
- VisualVM:VisualVM(All-in-One Java Troubleshooting Tool)是Oracle提供的一个功能强大的多合一故障诊断和性能监控的可视化工具。它集成了多个JVM调优的监控工具、故障排查工具、可视化工具等。
其他还有MAT (Memory Analyzer Tool)、JProfiler、Arthas、Btrace等