JVM面试汇总

JVM面试37问()
1入门部分
1.1为什么要学习JVM?
1、深入理解JVM可以帮助我们从平台角度提高解决问题的能力,例如:
有效防止内存泄漏(memory leak)
优化线程锁的使用(thread lock)
科学进行垃圾回收(Garbage collection)
提高系统吞吐量(throughput)
降低延迟(delay),提高性能(performance)
2、更好地理解资源分配,进行调优和故障排查,写出更加精巧,效率更高的代码,提高整体运行性能

1.2你了解哪些JVM产品?
常见的有HotSpot,JRockit,J9,TabobaoVM,Dalvik
Java Hotspot虚拟机是基于JVM规范的一种基本实现,在JVM规范中的方法区,在JDK8的HotSpot虚拟机中称之为元空间Metaspace.
GraalVM是Oracle发布的下一代Java虚拟机,2019.05才发布了第一个release版本,分别有社区版和企业版:
GraalVM有三大特点
1、高性能运行java(使用GraalVM执行java程式可以变得更快)。
2、多语言并行(可以在java里面同时使用多种语言,像是JavaScript、R)
3、快速启动(直接把Java应用编译成机器码,执行起来体积更小、启动速度更快)

1.3JVM的构成有哪几部分?
类加载系统(classloader system):负责加载类到内存。
运行时数据区(runtime data area):负责存储数据信息(对象、方法等)。
执行引擎(execution engine):负责解释执行字节码、执行GC操作等。
本地库接口(native interface):负责融合不同的编程语言为Java所用。

2类加载部分

2.1你知道哪些类加载器?
1、启动类加载器(bootstrap classloader)用来加载java核心类库,无法被java程序直接引用。
2、扩展类加载器(extensions classloader)用来加载java的扩展库,Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载java类。
3、系统类加载器(system classloader)也叫应用类加载器:它根据java应用的类路径(classpath)来加载java类。一般来说,java应用的类都是由它来完成的。可以通过ClassLoader.getSystemClassLoader()来获取它。
4、用户自定义类加载器,通过继承java.lang.classloader类的方式实现。

2.2什么是双亲委派类加载模型?
如果一个类加载器接收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类加载器去执行;如果父类加载器还存在其他父类加载器,则进一步向上委托,依次递归,请求最终将达到顶层的启动类加载器;如果父类加载器可以完成加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模型。

2.3双亲委派方式加载类有什么优势、劣势?
优势:
1、避免类的重复加载,确保一个类的全局唯一性。
2、保护程序安全,防止核心API被随意篡改
劣势:
父级加载器无法加载子级类加载器路径中的类

2.4描述一些类加载时候的基本步骤是怎样的?
1、通过一个类的全限定名(类全名)来获取其定义的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中数据的访问入口。

2.5什么情况下会触发类的加载?
1、遇到new,getstatic,putstatic,invokestatic这四条指令
2、使用java.lang.reflect包的方法对类进行反射调用
3、初始化一个类的时候,如果发现其父类没有进行过初始化,则先初始化其父类(如果父类是接口的话,则不要求初始化父类);
4、当虚拟机启动时,用户需要指定一个执行的主类(包含main方法的那个类),虚拟机会先初始化这个类;

2.6类加载时静态代码块一定会执行吗?
不一定,类的加载方式有多种,并不是每一种都能执行静态代码块。只有静态代码块所在的类被加载时,静态代码块才会执行。

2.7如何理解类的主动加载和被动加载?
1、主动使用:会执行加载、连接、初始化静态域
2、被动使用:只执行加载、连接,不初始化类静态域

2.8为什么要自己定义类加载器,如何定义?
因为:
1、修改类的加载方式(打破类的双亲委派模型)
2、扩展加载源(例如从数据中加载类)
3、防止源码泄露(对字节码文件进行加密,用时通过自定义类加载器对其进行解密)
4、隔离类的加载(不同框架有相同全限定名的类)
定义方式:
1、继承URLClassLoader,此类可以直接从指定目录、jar包、网络中加载指定的类资源。
2、通过继承java.lang.ClassLoader抽象类的方式,实现自己的类加载器,以满足一些特殊的需求。

3字节码增强部分
3.1为何要学习字节码?
了解字节码可以更准确、直观地理解Java语言中更深层次的东西,比如通过字节码可以很直观地看到Volatile关键字如何在字节码上生效。另外,字节码增强技术在Spring AOP、各种ORM框架、热部署中的应用也非常广泛。此外,由于JVM 规范的存在,只要最终可以生成符合规范的字节码就可以在JVM上运行,因此就给了各种运行在JVM上的语言一种契机,可以扩展Java所没有的特性,理解字节码后,可以从字节码视角看它的设计思路,也可以帮助我们更好地理解程序的运行。

3.2如何解读字节码内容?
1、直接解读,.java源代码编译后,可以通过notepad++打开.class文件,文件内容默认是一种16进制的格式,JVM对于字节码是有规范要求的,看似杂乱的十六进制是符合一定结构规范的。JVM规范要求每一个字节码文件都要按照固定的顺序组成。
2、javap指令应用,对类进行反编译,反编译后看到的十六进制操作码所对应的助记符,十六进制操作码与助记符的对应关系,以及每一个操作码的用处,均可在Oracle官方文档了解到。
3、在idea中使用jclasslib插件查看当前字节码文件的类信息、常量池、方法区信息。

3.3字节码内容由哪几部分构成?
1、魔数:所有.class文件的前四个字节都是魔数(magic Number),是class文件的标识。魔数的固定值为:0XCAFEBABE.魔数在文件开头,JVM可以根据魔数判断.class文件的合法性。
2、版本号:魔数之后的四个字节,前两个字节表示次版本号(Minor Version),后两个字节表示主版本号(Major Version).
3、常量池:版本号后面是常量池数量,用两个字节表示。常量池中存储两类常量:字面量(literal)与符号引用(symbolic references)。字面量为代码中声明为Final的常量值,符号引用如类和接口的全局限定名、字段的名称和描述符、方法的名称和描述符。
4、访问标识:常量池结束之后的两个字节,描述该Class是类还是接口,以及是否被Public、Abstract、Final等修饰符修饰。
5、类型引用:在访问标记之后,会指定该类的类别、父类类别以及实现的接口。
6、字段表集合:用于描述类和接口中的变量,包含类级别的变量以及实例变量,但是不包含方法内部声明的局部变量。
7、方法表集合:字段表结束后为方法表,用于描述每个方法的信息。第一部分为两个字节描述方法的个数;第二部分为每个方法的详细信息,包括访问标志、方法名、方法的描述以及方法的属性。
8、属性表集合:方法表结束后是属性表集合,用于描述class文件所携带的辅助信息,比如class文件对应的源文件信息。

3.4什么是字节码增强?
字节码增强技术相当于是一把打开运行时JVM的钥匙,利用它可以对现有字节进行修改或者动态生成新的字节码,进而对运行中的程序做修改、实现热部署。也可以跟踪JVM运行中的程序状态,进行性能诊断。

3.5为什么要进行字节码增强?
掌握字节码增强后可以高效地定位并快速修复一些棘手地问题(如线上性能问题、方法出现不可控的出入需要紧急加载日志等问题),也可以在开发中减少冗余代码,大大提高开发效率。

3.6你了解哪些字节码增强技术?
1、ASM技术:可以实现手动操作字节码的需求,可以使用ASM,它可以直接生产.class字节码文件,也可以在类被加载入JVM之前动态修改类行为。ASM的应用场景有AOP(Cglib就是基于ASM)、热部署、修改其他jar包中的类等。
2、Javassist技术:一个用于分析、编辑和创建java字节码的类库,相比ASM在指令层次上操作字节码会更加简单直观。可以无须关注字节码的刻板结构,直接使用java编码的形式,而不需要虚拟机指令,就能动态改变类的结构或者动态生成类。
3、Java Agent技术:提供了向现有已编译的java类添加字节码的功能,相当于字节码插桩的入口,可以侵入运行在JVM上的应用程序,进而修改应用程序中各类的字节码。

3.7什么是热替换以及如何实现?
1、定义:在不停止正在运行系统的情况下进行类的升级替换
2、实现:
(1)、实现自己的类加载器,并在其加载过程中进行控制和管理
(2)、通过不同的类加载器加载该类的不同版本
(3)、实现系统中同名类的不同版本实例的共存

4JVM运行内存部分
4.1JVM运行内存是如何划分的?
程序计数器、Java虚拟机、本地方法栈、堆、方法区
4.2JVM中的程序计数器用于做什么?
用来存储指向下一条指令的地址,记录当前线程执行的字节码的行号
4.3JVM虚拟机栈的结构是怎样的?
局部变量表(local variables)、操作数栈(operand stack)、动态链接(dynamic linking)
方法返回地址(return address)、附加信息
4.4JVM虚拟机栈中局部变量表的作用是什么?
用于存放方法参数和方法内部定义的局部变量信息
4.5JVM虚拟机栈中操作数栈的做用时什么?
在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)和出栈(pop)
4.6JVM堆的构成是怎样的?
java堆内存在JVM中可分为年轻代和老年代,年轻代又分为Eden和两个Survivor区
4.7Java对象分配内存的过程是怎样的?
1、编译器通过逃逸分析,确定对象是在栈上分配还是在堆上分配。
2、如果是在堆上分配,首先检测是否可在TLAB(Thread local allocation buffer)上直接分配。
3、如果TLAB上无法直接分配在Eden枷锁区分配(线程共享区).
4、如果Eden区无法存储对象则执行Yong GC(Minor Collection)。
5、如果Yong GC之后Eden区仍不足以存储对象,则直接分配在老年代。说明:在对象创建时可能会触发Yong GC。
6、新生代由Eden区和两个幸存区构成(s1,s2),任意时刻至少有一个幸存区是空的(empty),用于存放下次GC时未被收集的对象。
7、GC触发时Eden区所有可达对象会被复制到一个幸存区,假设为s1,当幸存区s1无法存储这些对象时会直接复制到老年代。
8、GC再次触发时Eden区和s1幸存区中的可达对象会被复制到另一个幸存区s2,同时清空eden区和s1幸存区。
9、GC再次触发时Eden区和s2幸存区中的可达对象会被复制到另一个幸存区s1,同时清空eden区和s2幸存区,以此类推;
10、当多次GC过程完成后,幸存区中的对象存活时间达到了一定阈值(可以用参数-XX:+MaxTenuringThreshold来指定上限,默认15),会被看成“老年”的对象然后直接移动到老年代。
4.8JVM年轻代幸存区设置的比较小会有什么问题?
伊甸园区被回收时,对象要拷贝到幸存区,假如幸存区比较小,拷贝的对象比较大,对象就会直接存储到老年代,这样会增加老年代的GC频率。而分代回收的思想就会被弱化。
4.9JVM年轻代伊甸园区设置的比例比较小会有什么问题?
伊甸园设置的比较小,会增加GC的频率,可能会导致STW的时间变长,影响系统性能。
4.10JVM堆内存为什么要分成年轻代和老年代?
为了更好地实现垃圾回收,优化GC性能。
4.11如何理解JVM方法区以及它的构成是怎样的?
方法区是一种规范,用于存储已被虚拟机加载的类信息、常量、静态变量、以及编译后的代码等数据。不同的jdk,方法区的实现不同。
构成分析:
1、类信息包括对每个加载的类型以及属性和方法信息
2、常量信息可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。
4.12什么是逃逸分析以及可以解决什么问题?
逃逸分析是一种数据分析算法,基于此算法可以有效减少java对象在堆内存中的分配。Hotspot虚拟机的编译器能够分析出一个新对象的引用范围,然后决定是否将这个对象分配到堆上。
使用逃逸分析,可以解决如下问题:
1、同步省略,如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
2、将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。
3、分离对象或标量替换。有的对象可能不需要作为一个连续结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。
4.13何为内存溢出以及导致内存溢出的原因?
内存中剩余的内存不足以分配给新的内存请求就会内存溢出。内存溢出可能直接导致系统崩溃。
内存泄漏是导致内存溢出的一种原因,但内存溢出不全是内存泄漏引起的,还可能是:
1、创建的对象太大导致内存溢出
2、创建的对象太多导致内存溢出
3、方法出现了无限递归调用导致栈内存溢出
4、方法区内存空间不足导致内存溢出
4.14何为内存泄漏以及内存泄漏的原因是什么?
动态分配的内存空间,在使用完毕后未得到释放,结果导致一直占据该内存单元,直到程序结束。这个现象称之为内存泄漏。内存泄漏会导致,程序变卡,性能严重下降,outofmemoryerror错误,系统直接挂掉。
导致内存泄漏的原因:
1、大量使用静态变量(静态变量与程序生命周期一样)
2、IO/链接资源用完没关闭(记得执行close操作)
3、内部类的使用方式存在问题(实例内部类或默认引用外部类对象)
4、缓存(cache)应用不当(尽量不要使用强引用)
5、ThreadLocal应用不当(用完记得执行remove操作)

4.15JAVA中的四大引用你知道多少?
1、强引用:无论在任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
2、软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它。
3、弱引用:用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。
4、虚引用:也成为“幽灵引用”或者“幻影引用”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。设置虚引用的唯一目的是为了能在这个对象被收集器回收时收到一个系统通知。

5JVM垃圾回收部分
5.1何为GC以及为和要GC?
1、GC(garbage collection)称之为垃圾回收,是对内存中的垃圾对象,采用一定的算法进行内存回收的一个动作。例如,Java中的垃圾回收会对内存中的对象进行遍历,对存活的对象进行标记,其未标记对象可人为是垃圾对象,然后基于特定算法进行回收。
2、深入理解GC的工作机制,可以写出更好的java应用,提高开发效率,为大规模应用开发做好准备。

5.2你知道哪些GC算法?
1、标记清除:先标记内存中所有不需要回收的对象,然后从内存中清除所有未标记的对象。
2、标记复制:将内存分为大小相同的两块,当这一块使用完了,就把当前存活的对象复制到另一块,然后一次性清空当前区块。
3、标记整理:从根节点开始标记所有被引用对象;遍历整个堆,把存活对象“压缩”复制到堆的其中任一块空间,按顺序排放;清理掉存活边界以外的全部内存空间。

5.3JVM中有哪些垃圾回收器?
1、Serial收集器:最古老最基本的收集器,但现在依然广泛使用。内部只使用一个线程执行垃圾回收(不能充分利用CPU的多核特性),无法并行化;GC时所有正在执行的用户线程暂停并且可能会产生较长时间的停顿(stop the world).
2、Parallel收集器: 并行收集器,可利用多个或多核CPU优势实现多线程并行GC操作,其目标是减少停顿时间,实现更高的吞吐量。
3、CMS收集器:mostly concurrent mark and sweep garbage collector,目标是追求更快的响应时间。
4、G1收集器:一种工作于服务端模式的垃圾回收器,主要面向多核,大内存的服务器。G1在实现高吞吐的同时,也最大限度满足了GC停顿时间可控的目标。

5.4服务频繁fullgc,younggc次数较少,可能原因?
1、系统承载高并发请求,或者处理数据量过大,导致Young GC 很贫乏,而且每次Young GC过后存活对象太多,内存分配不合理,survivor区过小,导致对象频繁进入老年代,频繁触发full GC。
2、系统一次性加载过多数据进内存,搞出来很多大对象,导致频繁有大对象进入老年代,必然频繁触发Full GC.
3、系统发生了内存泄漏,莫名其妙创建大量对象,始终无法回收,一直占用在老年代里,必然频繁触发Full GC。
4、Metaspace(永久代)因为加载类过多触发Full GC。
5、误调用System.gc()触发Full GC。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值