Hello,好久不见,我是圆圆。
今天为大家分享JVM,收集了很多,重点介绍面试过程中常见的JVM题目。将面试题分为三大类:基础题目,进阶题 目,实战题目。
感兴趣的小伙伴们,可以关注我,或者通过搜索微信公众号:程序猿媛加油 了解更多知识点,还有有趣的生活类知识。
OK~话不多说,直接给内容吧:
基础题目
1.1 JDK、JRE、JVM的关系是什么?
什么是JVM?
英文名称(Java Virtual Machine),就是JAVA虚拟机,它只识别.class类型文件,它 能够将class文件中的字节码指令进行识别并调用操作系统向上的API完成动作。
什么是JRE?
英文名称(Java Runtime Environment) , Java运行时环境。它主要包含两个部分: JVM的标准实现和Java的一些基本类库。相对于JVM来说,JRE多出来一部分Java类 库。
什么是JDK?
英文名称(Java Development Kit) , Java开发工具包。JDK是整个Java开发的核心, 它集成了 JRE和一^好用的小工具。例如:javac.exe、java.exe、jar.exe等。
这三者的关系:一层层的嵌套关系。JDK>JRE>JVMO
1.2 JVM的内存模型以及分区情况和作用
如下图所示:
黄色部分为线程共有,蓝色部分为线程私有。
方法区
用于存储虚拟机加载的类信息,常量,静态变量等数据。
堆
存放对象实例,所有的对象和数组都要在堆上分配。是JVM所管理的内存中最大的一块 区域。
栈
Java方法执行的内存模型:存储局部变量表,操作数栈,动态链接,方法出口等信息。 生命周期与线程相同。
本地方法栈
作用与虚拟机栈类似,不同点本地方法栈为native方法执行服务,虚拟机栈为虚拟机执 行的Java方法服务。
程序计数器
当前线程所执行的行号指示器。是JVM内存区域最小的一块区域。执行字节码工作时就 是利用程序计数器来选取下f需要执行的字节码指令。
1.3 JVM对象创建步骤流程是什么?
整体流程如下图所示:
第1步:虚拟机遇到一个 new指令,首先将去检查这个指令的参数是否能在常量池中定 位到这个类的符号弓I用,并且检查这个符号弓I用的类是否已经被加载&解析&初始化
第2步:如果类已经被加载那么进行第3步;如果没有进行加载,那么就就需要先进行 类的加载。
第3步:类加载检查通过之后,接下来进行新生对象的内存分配。
第4步:对象生成需要的内存大小在类加载完成后便可完全确定,为对象分配空间等同 于把一块确定大小的内存从Java堆中划分出来
第5步:内存大小的划分分为两种情况:
第一种情况:JVM的内存是规整的,所有的使用的内存都放到一边,空闲的内存在另外 一边,中间放一个指针作为分界点的指示器。那么这时候分配内存就比较简单,只要 讲指针向空闲空间那边挪动一段与对象大小相同的距离。这种就是“指针碰撞“。
第二种情况:JVM的内存不是规整的,也就是说已使用的内存与未使用的内存相互交 错。这时候就没办法利用指正碰撞了。这时候我们就需要维护一张表,用于记录那些内 存可用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新到记录 表上。
第6步:空间申请完成之后,JVM需要将内存的空间都初始化为0值。如果使用TLAB, 就可以在TLAB分配的时候就可以进行该工作。
第7步:JVM对对象进行必要的设置。例如,这个对象是哪个类的实例、对象的哈希 码、GC年代等信息。
第8步:完成了上面的步骤之后从JVM来看一个对象基本上完成了,但从Java程序代 码绝对来看,对象创建才刚刚开始,需要执行<init>方法,按照程序中设定的初始化 操作初始化,这时候一个真正的程序对象生成了。
1.4垃圾回收算法有几种类型?他们对应的优缺点又是什么?
常见的垃圾回收算法有:
标记-清除算法、复制算法、标记-整理算法、分代收集算法
标记-清楚算法
标记-清楚算法通括两个阶段:“标记”和“清除”。
标记阶段:确定所有要回收的对象,并做标记。
清除阶段:将标记阶段确定不可用的对象清除。
- 标讶口清除的效率都不高。
- 会产生大量的碎片,而导致频繁的回收。
复制算法
内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候, 把存活的对象复制到另一块上,然后把这块内存整个清理掉。
缺点:
- 需要浪费额外的内存作为复制区。
- 当存活率较高时,复制算法效率会下降。
标记—整理算法
标记一整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移 动,然后直接回收边界以外的内存。
缺点:
算法复杂度大,执行步骤较多
分代收集算法:
目前大部分JVM的垃圾收集器采用的算法。根据对象存活的生命周期将内存划分为若干 个不同的区域。—般情况下将堆区划分为新生代(Young Generation和老年代(Tenured Generation ),永久代(Permanet Generation ) „
老年代的特点是每次垃圾唉时只有少量对象需要被回收,而新生代的特点是每次垃圾 回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算 法。
如下图所示:
Young:存放新创建的对象,对象生命周期非常短,几乎用完可以立即回收,也叫Eden 区。
Tenured: young区多次回收后存活下来的对象将被移到 tenured区,也叫old区。
Perm:永久带,主要存加载的类信息,生命周期长,几乎不会被回收。
缺点:
算法复杂度大,执行步骤较多。
1.5简单介绍一下什么是类加载机制?
Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过 该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等。
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始 化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
1.6 类的加载过程是什么?简单描述一下每个步骤
类加载的过程包括了:
加载、验证、准备、解析、初始化五个阶段
第一步:加载
查找并加载类的二进制数据。
加载是类加载过程的第1个阶段,虚拟机在这一阶段需要完成以下三件事情:
——通过类的全限定名来获取其定义的二进制字节流
——将字节流所代表的静态存储结构转化为方法区的运行时数据结构
——在Java堆中生成一个代表这个类的java.Iang.Class对象,作为对方法区中这些数据 的访问入口
第二步:验证
确保被加载的类的正确性。
这一阶段是确保Class文件的字节流中包含的信息符合当前虚拟机的规范,并且不会损 害虚拟机自身的安全。包含了四个验证动作:文件格式验证,元数据验证,字节码验 证,符号引用验证。
第三步:准备
为类的静态变量分配内存,并将其初始化为默认值。
准备阶段是正式为类变量分配内存并设置内变量初始值的阶段,这些内存都将在方法区 中分配。
第四步:解析
把类中的符号引用转换为直接引用。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类 或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用 进行。
第五步:初始化
类变量进行初始化
为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始 化。
1.7 JVM预定义的类加载器有哪几种?分别什么作用?
启动(Bootstrap)类加载器、标准扩展(Extension)类加载器、应用程序类加载器 (Application)
启动(Bootstrap)类加载器
引导类装入器是用本地代码实现的类装入器,它负责将< Java_Runtime_Home >/lib下面 的类库加载到内存中。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接 获取到启动类加载器的引用。
标准扩展(Extension)类加载器
扩展类加载器负责将< Java_Runtime_Home >/lib/ext或者由系统变量java.ext.dir指定位 置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
应用类加(Application)
应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类 库。
1.8 什么是双亲委派模式?有什么作用?
双亲委派模型的工作流程是:如果一个类加载器收到了内加载的请求,它首先不会自己 去加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器没有找到所需的类时, 子加载器才会尝试去加载该类。
双亲委派机制:
- 当AppCIassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把 类加载请求委派给父类加载器ExtClassLoader去完成。
- 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是 把类加载请求委派给BootStrapCIassLoader去完成。
- 如果BootStrapCIassLoader加载失败,会使用ExtClassLoader来尝试加载;
- 若ExtClassLoader也加载失败,则会使用AppCIassLoader来加载,如果 AppCIassLoader也加载失败,则会报出异常 ClassNotFoundException
如下图所示:
双亲委派作用:
-通过带有优先级的层级关可以避免类的重复加载;
.保证Java程序安全稳定运行,Java核心API定义类型不会被随意替换。
1.9 介绍一下JVM中垃圾收集器有哪些?他们特点分别是什么?
新生代垃圾收集器
Serial收集器
特点:
Serial收集器只能使用一条线程进行垃圾收集工作,并且在进行垃圾收集的时候,所有的工作线程都需要停止工作,等待垃圾线程收集完成以后,其他线程才可以继续工作。
使用算法:复制算法
ParNew收集器
特点:
ParNew垃圾收集器是Serial收集器的多线程版本。为了利用CPU多核多线程的优势, ParNew收集器可以运行多个收集线程来进行垃圾收集工作。这样可以提高垃圾收集过程 的效率。
使用算法:复制算法
Parallel Scavenge 收集器
特点:
Parallel Scavenge收集器是一款多线程的垃圾收集器,但是它又和ParNew有很大的不同
Parallel Scavenge收集器和其他收集器的关注点不同。其他收集器,比如ParNew和CMS 这些收集器,它们主要关注的是如何缩短垃圾收集的时间。而Parallel Scavenge收集器 关注的是如何控制系统运行的吞吐量。这里说的吞吐量,指的是CPU用于运行应用程序 的时间和CPU总时间的占比,吞吐量=代码运行时间/ (代码运行时间+垃圾收集时 间).如果虚拟机运行的总的CPU时间是100分钟,而用于执行垃圾收集的时间为1分 钟,那么吞吐量就是99%。
使用算法:复制算法
老年代垃圾收集器
Serial Old收集器
特点:
Serial Old收集器是Serial收集器的老年代版本。这款收集器主要用于客户端应用程序中 作为老年代的垃圾收集器,也可以作为服务端应用程序的垃圾收集器。
使用算法:标记-整理
CMS收集器
特点:
CMS收集器是目前老年代收集器中比较优秀的垃圾收集器。CMS是Concurrent Mark Sweep,从名字可以看出,这是一款使用”标记-清除”算法的并发收集器。
CMS垃圾收集器是一款以获取最短停顿时间为目标的收集器。如下图所示:
从图中可以看出,CMS收集器的工作过程可以分为4个阶段:
•初始标记(CMS initial mark)阶段
•并发标记(CMS concurrent mark)阶段
•重新标记(CMS remark)阶段
•并发清除((CMS concurrent sweep)阶段
使用算法:复制+标记清除
其他
G1垃圾收集器
特点:
主要步骤:初始标记,并发标记,重新标记,复制清除。
使用算法:复制+标记整理
1.10什么是Class文件? Class文件主要的信息结构有哪些?
Class文件是一组以8位字节为基础单位的二进制流。各个数据项严格按顺序排列。
Class文件格式采用一种类似于C语言结构体的伪结构来存储数据。这样的伪结构仅仅有 两种数据类型:无符号数和表。
无符号数:是基本数据类型。以ul、u2、u4、U8分别代表1个字节、2个字节、4个字 节、8个字节的无符号数,能够用来描写叙述数字、索引引用、数量值或者依照UTF-8 编码构成的字符串值。
表:由多个无符号数或者其它表作为数据项构成的复合数据类型。全部表都习惯性地以 _ionfo结尾。
1.11对象“对象已死”是什么概念?
对象不可能再被任何途径使用,称为对象已死。
判断对象已死的方法有:引用计数法与可达性分析算法。