- **一、入门部分**
- 1.1 为什么要学习JVM?
- 1.2 你了解哪些JVM产品?
- 1.3 JVM的构成有哪几部分?
- **二、类加载部分**
- 2.1 你知道哪些类加载器?
- 2.2 什么是双亲委派类加载模型?
- 2.3 双亲委派方式加载类有什么优势、劣势?
- 2.4 描述一些类加载时候的基本步骤是怎样的?
- 2.5 什么情况下会触发类的加载?
- 2.6 类加载时静态代码块一定会执行吗?
- 2.7 如何理解类的主动加载和被动加载?
- 2.8 为什么要自己定义类加载器,如何定义?
- **三、 字节码增强部分**
- 3.1 为何要学习字节码?
- 3.2 如何解读字节码内容?
- 3.3 字节码内容由哪几部分构成?
- 3.4 什么是字节码增强?
- 3.5 为什么要进行字节码增强?
- 3.6 你了解哪些字节码增强技术?
- 3.7 什么是热替换以及如何实现?
- **四、JVM运行内存部分**
- 4.1 JVM运行内存是如何划分的?
- 4.2 JVM中的程序计数器用于做什么?
- 4.3 JVM虚拟机栈的结构是怎样的?
- 4.4 JVM虚拟机栈中局部变量表的作用是什么?
- 4.5 JVM虚拟机栈中操作数栈的做用时什么?
- 4.6 JVM堆的构成是怎样的?
- 4.7 Java对象分配内存的过程是怎样的?
- 4.8 JVM年轻代幸存区设置的比较小会有什么问题?
- 4.9 JVM年轻代伊甸园区设置的比例比较小会有什么问题?
- 4.10 JVM堆内存为什么要分成年轻代和老年代?
- 4.11 如何理解JVM方法区以及它的构成是怎样的?
- 4.12 什么是逃逸分析以及可以解决什么问题?
- 4.13 何为内存溢出以及导致内存溢出的原因?
- 4.14 何为内存泄漏以及内存泄漏的原因是什么?
- 4.15 JAVA中的四大引用你知道多少?
- **五、JVM垃圾回收部分**
- 5.1 何为GC以及为和要GC?
- 5.2 你知道哪些GC算法?
- 5.3 JVM中有哪些垃圾回收器?
- 5.4 服务频繁fullgc,younggc次数较少,可能原因?
- 5.4 服务频繁fullgc,younggc次数较少,可能原因?
今年的java市场可谓风卷残云,作为java开发的我们,没有JVM技能傍身可谓寸步难行,笔者在这里整理了全套JVM基础的面试问题,供大家学习~
一、入门部分 1.1 为什么要学习JVM?答:java程序是在JVM上运行的,理解JVM能够让我们了解java程序的底层运行原理,有助于我们更好的学习java,也有助于我们写出更加优质的代码,并且能够针对java运行时的一些特点进行JVM调优。
1.2 你了解哪些JVM产品?答:HotSpot虚拟机,JRockit虚拟机,J9虚拟机,TaobaoVM虚拟机。
1.3 JVM的构成有哪几部分?答:分为类加载子系统、运行时内存区,执行引擎、本地方法接口。
二、类加载部分 2.1 你知道哪些类加载器?答:AppClassLoader类加载器,ExtClassLoader类加载器,BootStrapClassLoader类加载器
2.2 什么是双亲委派类加载模型?答:当需要加载一个类的时候,类加载器先向上级询问“是否已经加载此类”,如果一直询问到最上层即BootStrapClassLoader加载器时都反馈“未加载此类”,那么依次向下级判断“是否能够加载此类”,如果能够加载,则由当前询问到的加载器进行加载。
2.3 双亲委派方式加载类有什么优势、劣势?答:双亲委派加载方式的优势:由于向上询问,向下加载的机制,使得这种类加载机制可以避免重复加载类对象,有利于程序的安全运行;双亲委派模型的劣势:由于每次都要进行询问、加载的判断,对于程序运行效率会有部分影响,且不能在一个系统中加载名称相同的两个类。
2.4 描述一些类加载时候的基本步骤是怎样的?答:1.通过一个类的全限定名来获取其定义的二进制字节流;2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;3.在java堆中生成一个代表这个类的对象,作为方法区中这些数据的访问入口。
2.5 什么情况下会触发类的加载?答:加载类的情况主要有两种:隐式加载和显示加载:
隐式加载:1.访问类的静态成员;2.构件类的实例对象;3.构件子类实例对象;
显示加载:1.通过ClassLoader.loadClass()方法进行加载;2.通过Class.forName()方法进行加载。
2.6 类加载时静态代码块一定会执行吗?答:不一定,当主动加载类的时候,静态代码块会执行,但是隐式加载类的时候,不会执行静态代码块。
2.7 如何理解类的主动加载和被动加载?答:主动加载就是我们主动去使用一个类的成员方法,或者主动去构建其实例对象,这对于程序而言就是主动加载;而被动加载,则是我们通过反射机制去手动加载,对于程序而言就是被动加载。
2.8 为什么要自己定义类加载器,如何定义?答:因为系统提供的类加载器不能满足我们的需求,所以需要使用我们自己的类加载器进行加载;定义方法有两种:一种是继承URLClassLoader,然后重写其构造方法即可;另一种是继承CLassLoader,重写其FindClass方法。
三、 字节码增强部分 3.1 为何要学习字节码?答:可以深入理解java程序的运行过程,有助于我们写出效率更高的代码;并且我们也可以利用字节码增强技术,实现程序的热部署;也可以跟踪程序JVM程序运行中的状态,进行性能诊断。
3.2 如何解读字节码内容?答:可以通过notepad++的插件进行解读;也可以根据javap -p反编译命令进行解读;也可以使用IDEA的JClasslib插件进行解读。
3.3 字节码内容由哪几部分构成?答:魔数、版本号、常量池、访问标识、类型引用、字段表集合、方发表集合、属性表集合组成。
3.4 什么是字节码增强?答:字节码增强技术相当于是一把运行时JVM的钥匙,利用它可以对现有字节码进行修改或动态生成新的字节码,进而对运行中的程序进行修改,实现热部署,也可以跟踪JVM运行中程序的状态,进行性能诊断等。此外,我们平时使用的动态代理、AOP也与字节码增强密切相关,它们实质上还是利用各种手段生成符合规范的字节码文件。综上所述,掌握字节码增强后可以高效地定位并快速修复一些棘手的问题(如线上性能问题、方法出现不可控的出入参需要紧急加日志等问题),也可以在开发中减少冗余代码,大大提高开发效率。
3.5 为什么要进行字节码增强?答:因为使用字节码增强技术,可以在不停止程序的前提下,实现程序功能的增强。
3.6 你了解哪些字节码增强技术?答:ASM技术,Javassist技术,Java Agent技术。
3.7 什么是热替换以及如何实现?答:热替换是在不停止程序运行的前提下,对程序的功能进行增强修改的一种技术。实现过程:首先需要在被增强的程序中设置开启运行时允许类对象的修改,然后使用ASM或Javassist技术对已经加载的class代码进行增强,之后把这部分对已运行系统进行增强的代码打包成jar,通过Tools工具包中的API指向需要功能增强的程序进行运行,就可以实现字节码增强。
四、JVM运行内存部分 4.1 JVM运行内存是如何划分的?答:分为程序计数器,栈内存,堆内存,和方法区。
4.2 JVM中的程序计数器用于做什么?答:程序计数器用来记录每个线程运行到的代码位置,由于各个线程在进行运行的时候都是随机取的时间片,然后在时间片的时间内进行运行,那么使用程序计数器可以让取的时间片的程序知道上次运行结束的位置,然后可以继续运行。
4.3 JVM虚拟机栈的结构是怎样的?答:栈中包含有栈针对象,栈针对象中包含有:操作栈,局部变量表,动态链接和方法返回值,还包含部分附加信息。
4.4 JVM虚拟机栈中局部变量表的作用是什么?答:用于存放方法参数和方法内部定义的局部变量信息。
4.5 JVM虚拟机栈中操作数栈的做用时什么?答:在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)和 出栈(pop)。
4.6 JVM堆的构成是怎样的?答:JVM中的堆分为新生代和老年代,其中新生代分为伊甸园区、幸存者1区和幸存者2区。
4.7 Java对象分配内存的过程是怎样的?答:1.编辑器进行逃逸分析,判断对象是在栈上分配还是在堆中分配;2.如果是在堆中分配,则首先检测是否可在TLAB上直接分配;3.如果无法在TLAB上分配,则在伊甸园区进行分配;4.如果伊甸园区无法存储对象,则进行Young GC;如果Young GC后仍然无法存储对象,则在老年代中存储该对象。
4.8 JVM年轻代幸存区设置的比较小会有什么问题?答:如果年轻代幸存区设置的比较小,那么在执行Young GC后,如果在幸存区放不下,那么会直接放入老年代,这样会增加老年代GC的频率,而分带回收的思想就会被弱化。
4.9 JVM年轻代伊甸园区设置的比例比较小会有什么问题?答:伊甸园区设置的比例较小,会增加GC的频率,影响系统性能。
4.10 JVM堆内存为什么要分成年轻代和老年代?答:遵循分代回收的思想,根据对象可能存活时间的不同,分别设置了对应的GC机制,可以更有效率的进行垃圾回收。
4.11 如何理解JVM方法区以及它的构成是怎样的?答:方法区是存储已被虚拟机加载的类的信息、常量、静态变量,即编译后的代码等数据。HotSpot虚拟机在JDK8中方法区落地实现采用了元数据区,即本地内存。
方法区的特点:1.方法区是多个线程共享的空间,多个线程加载同一个类时,只能加载一次;2.方法区在JVM启动时被创建,同堆内存一样是不连续的空间;3.方法区可以手动指定大小;4.当创建的类足够多时,会导致方法区内存溢出,会抛出内存溢出错误。
方法区包含:1.每个加载的类的类型、属性和方法的信息;2.方法区中的类的常量信息可以看做是一张表,虚拟机可以根据这张表找到要执行的类名、方法名、参数类型、字面量等信息。
4.12 什么是逃逸分析以及可以解决什么问题?答:逃逸分析就是判断在方法中声明的变量是否被外界所引用。逃逸分析的作用:1.如果一个变量没有在该方法之外被使用,那么就可以将其放置在栈内存中,这样可以减少堆内存GC的次数,提高程序运行效率;2.可以进行同步锁消除,消除方法中没有必要的同步锁;3.可以对变量进行标量替换,即通过标量替换的方式,将对象分配到栈上,减少堆内存的GC,提高运行效率。
4.13 何为内存溢出以及导致内存溢出的原因?答:内存溢出就是:内存中剩余的内存不足以分配给新的内存请求。
内存溢出的原因:
1.创建的对象太大导致内存溢出;
2.创建的对象太多导致内存溢出;
3.方法出现了无限递归导致内存溢出;
4.方法区内存空间不足导致内存溢出。
4.14 何为内存泄漏以及内存泄漏的原因是什么?答:内存泄露就是:动态分配的内存空间,在使用完毕后未得到释放,仍旧占用的内存空间,直到程序结束。
内存泄露的原因:
1. 大量使用静态资源变量;
2. IO连接的资源没有及时关闭;
3. 内部类的使用方式存在问题(实例内部类默认引用外部类对象)
4. 缓存应用不当(尽量不要使用强引用);
5.ThreadLocal使用不当。
4.15 JAVA中的四大引用你知道多少?答:强引用,弱引用,软引用,虚引用。
强引用:
我们使用的最普遍的引用,如果一个对象具有强引用,那么当虚拟机内存不足时,虚拟机宁愿抛出OutOfMemoryError错误,也不愿随 意回收强引用。
软引用:
如果内存空间足够,就不会回收它;如果内存空间不足,就会回收这类对象的内存,只要垃圾回收期没有回收它,就可以一直被程序使 用。
弱引用:
当垃圾回收器扫描内存空间时,一旦发现只有弱引用的对象,不管当前内存空间足够与否,都会对其进行回收。在使用过程中,其可以 和引用队列(ReferenceQueue)一同使用。
虚引用:
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都有可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾 回收的活动。
五、JVM垃圾回收部分 5.1 何为GC以及为和要GC?答:GC为JVM对堆内存进行垃圾回收的一个动作。程序在运行过程中会不断地产生内存碎片,如果一直不进行GC操作,那么程序迟早会因为内存耗尽而抛出OutOfMemoryError错误而终止程序。
5.2 你知道哪些GC算法?答:标记清除、标记复制、标记整理。
标记清除:
此算法分为“标记”阶段和“清理”阶段,第一次遍历所有对象标记出不需要回收的对象,然后第二次对所有对象进行遍历清楚掉没有被标 记的对象。
标记复制:
此算法需要两块完全相同的内存空间,当算法开始时,另一块内存空间为空,算法对内存空间进行遍历时,将活着的对象移动到另一块 空内存中,结束之后清除掉原内存空间的所有数据,此方法适用于生命周期短、回收频率高的内存中,例如堆内存年轻代中的幸存者1 区和幸存者2区。
标记整理:
此算法分为三个阶段,第一阶段从根节点开始遍历所有对象,然后标记存活的对象;第二阶段遍历整个堆内存,讲活着的对象“整理”到 一起,按照顺序排放;第三阶段从存活边界开始清除掉其余剩下的所有对象。
5.3 JVM中有哪些垃圾回收器?答:分为串行收集器(Serial)、并行收集器(Parallel)、并发收集器(CMS)、G1收集器。
Serial特点:
1.只有一个线程进行垃圾回收,无法并行化;
2.GC时所有正在执行的用户线程暂停,并且有可能产生较长的停顿时间。故适合单CPU的小应用。
Parallel特点:
1.可以利用CPU的多核特性执行多线程下的并行化GC操作;
2.GC期间,所有内核共同清理垃圾,故所消耗时间较短;
3.最大优势是可实现可控制的吞吐量与停顿时间。
CMS特点:
1.使用空闲列表(free-lists)管理内存空间的回收,不对老年代进行碎片整理,减少用户线程暂停时间;
2.在标记-清除阶段的大部分工作和用户线程一起并发执行;
3.最大优点是可减少停顿时间(可提高服务的响应速度),最大缺陷是老年代的内存碎片。
G1收集器特点:
1.将java堆均分成大小相同的多个区域(region,1M-32M,最多2000个,最大支持堆内存64G);
2.内存应用具备极大地弹性(一个或多个不连续的区域共同组成eden、survivor或old区,但大小不再固定);
3.相对CMS有着更加可控的暂停时间(pause time) 和 更大的吞吐量(throughput)以及更少的碎片(标记整理);
4.支持并行与并发,可充分利用多CPU,多核优势,降低延迟,提高响应速度。
5.4 服务频繁fullgc,younggc次数较少,可能原因?答:1.年轻代的伊甸园区内存空间设置太小,导致新对象之间在堆内存中创建;
2.方法区设置过小,导致频繁触发方法区中的GC,即fullgc;
3.程序运行过程中出现内存泄露,使用完毕的内存不能及时释放;
4.堆内存大小设置过小,不能满足程序本身要求。
roughput)以及更少的碎片(标记整理);
4.支持并行与并发,可充分利用多CPU,多核优势,降低延迟,提高响应速度。
5.4 服务频繁fullgc,younggc次数较少,可能原因?答:1.年轻代的伊甸园区内存空间设置太小,导致新对象之间在堆内存中创建;
2.方法区设置过小,导致频繁触发方法区中的GC,即fullgc;
3.程序运行过程中出现内存泄露,使用完毕的内存不能及时释放;
4.堆内存大小设置过小,不能满足程序本身要求。
以上问题均为笔者回答,若有更好意见,欢迎在评论区留言或私信,愿与屏幕前的你共同进步~



