JVM面试37问

目录

JVM面试37问

一、入门部分

1、为什么要学习JVM?

2、你了解哪些JVM产品?

3、JVM的构成有哪几部分?

二、类加载部分

1、你知道哪些类加载器?

2、双亲委派方式加载类有什么优势、劣势?

3、描述一些类加载时候的基本步骤是怎样的?

4、什么情况下会触发类的加载?

5、类加载时静态代码块一定会执行吗?

6、如何理解类的主动加载和被动加载?

7、为什么要自己定义类加载器,如何定义?

三、字节码增强部分

1、为何要学习字节码?

2、如何解读字节码内容?

3、字节码内容由哪几部分构成?

4、什么是字节码增强?

5、为什么要进行字节码增强?

6、你了解哪些字节码增强技术?

7、什么是热替换以及如何实现?

四、JVM运行内存部分

1、JVM运行内存是如何划分的?

2、JVM中的程序计数器用于做什么?

3、JVM虚拟机栈的结构是怎样的?

4、JVM虚拟机栈中局部变量表的作用是什么?

5、JVM虚拟机栈中操作数栈的作用是什么?

6、JVM堆的构成是怎样的?

7、Java对象分配内存的过程是怎样的?

8、JVM年轻代幸存区设置的比较小会有什么问题?

9、JVM年轻代伊甸园区设置的比例比较小会有什么问题?

10、JVM堆内存为什么要分成年轻代和老年代?

11、如何理解JVM方法区以及它的构成是怎样的?

12、什么是逃逸分析以及可以解决什么问题?

13、何为内存溢出以及导致内存溢出的原因?

14、何为内存泄漏以及内存泄漏的原因是什么?

15、JAVA中的四大引用你知道多少?

五、JVM垃圾回收部分

1、何为GC以及为何要GC?

2、你知道哪些GC算法?

3、JVM中有哪些垃圾回收器?

4、服务频繁fullgc,younggc次数较少,可能原因?


JVM面试37问

  • 一、入门部分

1为什么要学习JVM?

第一:深入理解JVM可以帮助我们从平台角度提高解决问题的能力,

例如:

有效防止内存泄漏(Memory leak)。

优化线程锁的使用 (Thread Lock)。

科学进行垃圾回收 (Garbage collection)。

提高系统吞吐量 (throughput)。

降低延迟(Delay),提高其性能(performance)。

第二:企业面试需要(中高级程序员必备技能)

2、你了解哪些JVM产品?

1.HotSpot VM(Sun公司研发, 2010年由Oracle公司收购).

2.JRockit VM(BEA公司研发, 2008年由Oracle公司收购).

3.J9 VM(IBM内部使用).

4.TaobaoJVM(AliJVM团队开发, 基于OpenJDK开发了AlibabaJDK5).

5.Dalvik

3、JVM的构成有哪几部分?

类加载子系统

运行时数据区

执行引擎

本地方法接口

类加载部分

1、你知道哪些类加载器?

1)BootstrapClassLoader(启动类加载器/根类加载器)

        a、C++编写的,程序员无法在程序中获取该类
b、负责加载虚拟机的核心库,比如java.lang.Object
c、没有继承ClassLoader

负责将<Java_Home>/jre/lib下面的类库加载到内存中(比如/jre/lib/rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。不继承自java.lang.ClassLoader。

也加载-Xbootclasspath参数指定路径,但是必须是虚拟机识别的(仅按照文件名识别),否则就算放在/lib目录下,也不会被根类加载器加载。

根类加载器使用C++实现(HotSpot),是虚拟机的一部分。是唯一一个使用本地代码编写的加载器。

之前看到一个网友提问:“既然所有的Java类,都是由类加载器加载的,那么BootstrapClassLoader,首先是一个类,它是由谁来加载的呢?”。

其实BootstrapClassLoader,不是一个java类。我们在IDE工具中,也引用不了该类。它是在JVM启动时运行的一个特殊的系统类加载器。

由于BootstrapClassLoader对Java不可见,所以返回了null,我们也可以通过某一个类的加载器是否为null来作为判断该类是不是使用BootstrapClassLoader进行加载的依据

2)ExtClassLoader(扩展类加载器)

a、Java编写的,从指定目录中加载类库
b、父加载器是根类加载器
c、是ClassLoader的子类
d、如果用户把创建的jar文件放到指定目录中,也会被扩展加载器加载。

继承自java.lang.ClassLoader

加载<JAVA_HOME>/jre/lib/ext目录下的类,或者被java.ext.dirs系统变量指定的路径中的所有类库。它的父加载器是null(因为BootstrapClassLoader是使用C++实现的,没有对应的java类)

jdk8中,系统定义的包有:

1.cldrdata.jar

2.dnsns.jar

3.jaccess.jar

4.jfxrt.jar

5.localedata.jar

6.nashorn.jar

7.sunec.jar

8.sunjce_provider.jar

9.sunpkcs11.jar

10.zipfs.jar

3)AppClassLoader(应用程序类加载器,又称系统类加载器)

a、Java编写的
b、父加载器是扩展类加载器
c、从环境变量或者class.path中加载类
d、是用户自定义类加载的默认父加载器
e、是ClassLoader的子类

是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统(System)加载器。

4)用户自定义类加载器

在Java的日常应用程序开发中,类的加载几乎是上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,来定制类的加载方式。

自定义类加载器的目的:

(1)隔离加载类

(2)修改类加载的方式

(3)扩展加载源

(4)防止源码泄露

类加载器的关系

BootStrap启动类加载器是Extension扩展类加载器的父类,Extension扩展类加载器是APP应用程序类加载器的父类,APP应用程序类加载器是Custom ClassLoader自定义加载器的父类。

2、双亲委派方式加载类有什么优势、劣势?

优势:

     1.避免类的重复加载, 确保一个类的全局唯一性

Java 类随着它的类加载器一起具备了一种带有优先级的层级关系, 通过这种层级关系可以避免类的重复加载, 当父亲已经加载了该类时, 就没有必要子ClassLoader 再加载一次

2.保护程序安全,防止核心 API 被随意篡改

劣势:

1.无法扩展加载源.

2.无法实现对字节码文件进行加密后再通过类加载器对其进行解密.

3.无法实现隔离类的加载.

4.在某些场景下双亲委派制过于局限,所以有时候必须打破双亲委派机制来达到目的。例如:SPI机制

检查类是否加载的委派过程是单向的, 这个方式虽然从结构上说比较清晰,使各个 ClassLoader 的职责非常明确, 但是同时会带来一个问题, 即顶层的ClassLoader 无法访问底层的 ClassLoader 所加载的类,通常情况下, 启动类加载器中的类为系统核心类, 包括一些重要的系统接口,而在应用类加载器中, 为应用类。 按照这种模式, 应用类访问系统类自然是没有问题, 但是系统类访问应用类就会出现问题。 比如在系统类中提供了一个接口,该接口需要在应用类中得以实现, 该接口还绑定一个工厂方法, 用于创建该接口的实例, 而接口和工厂方法都在启动类加载器中。 这时, 就会出现该工厂方法无法创建由应用类加载器加载的应用实例

双亲委派机制原理

1.如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。

2.如果父类的加载器还存在其父类加载器,则进一步向上委托,依次递归请求最终达到顶层的启动类加载器。

3.如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制。

3、描述一些类加载时候的基本步骤是怎样的?

一般来说,我们把Java的类加载过程分为三个主要步骤:加载、链接、初始化,具体行为在Java虚拟机规范里有非常详细的定义。

加载阶段(Loading):它是Java将字节码数据从不同的数据源读取到JVM中,并映射为JVM认可的数据结构(Class对象)。这里的数据源可能是各种各样的形态,如jar文件、class文件,甚至是网络数据源等。如果输入数据不是ClassFile的结构,则会抛出ClassFormatError。

加载阶段是用户参与的阶段,我们可以自定义类加载器,去实现自己的类加载过程。

链接阶段(Linking):这是核心的步骤,简单说是把原始的类定义信息平滑地转化入JVM运行的过程中。这里可进一步细分为三个步骤:

  • 验证(Verification),这是虚拟机安全的重要保障,JVM需要核检字节信息是符合Java虚拟机规范的,否则就被认为是VerifyError。这样就防止了恶意信息或者不合规的信息危害JVM的运行。验证阶段有可能触发更多class的加载。
  • 准备(Preparation),创建类或接口中的静态变量,并初始化静态变量的初始值。但这里的“初始化”和下面的显式初始化阶段是有区别的,侧重点在于分配所需要的内存空间,不会去执行更进一步的JVM指令。
  • 解析(Resolution),在这一步会将常量池中的符号引用(symbolic reference)替换为直接引用。在Java虚拟机规范中,详细介绍了类、接口、方法和字段等各个方面的解析。

初始化(Initialization):这一步真正去执行类初始化的代码逻辑,包括静态字段复制的动作,以及执行类定义中的静态初始化块内的逻辑。编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。

4、什么情况下会触发类的加载?

以下情况会触发类的初始化:

  1. 遇到new,getstatic,putstatic,invokestatic这4条指令;
  2. 使用java.lang.reflect包的方法对类进行反射调用;
  3. 初始化一个类的时候,如果发现其父类没有进行过初始化,则先初始化其父类(注意!如果其父类是接口的话,则不要求初始化父类);
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类;
  5. 当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则先触发其类初始化;
  6. 调用静态成员时,会加载静态成员真正所在的类及其父类。 通过子类调用父类的静态成员时,只会加载父类而不会加载子类。
  7. 第一次 new 对象的时候 加载(第二次再 new 同一个类时,不需再加载)。
  8. 加载子类会先加载父类。(覆盖父类方法时所抛出的异常不能超过父类定义的范围)

注:如果静态属性有 final 修饰时,则不会加载,当成常量使用。

例:public static final int a =123;

但是如果上面的等式右值改成表达式(且该表达式在编译时不能确定其值)时则会加载类。

例:public static final int a = math.PI

如果访问的是类的公开静态常量,那么如果编译器在编译的时候能确定这个常量的值,就不会被加载;

如果编译时不能确定其值的话,则运行时加载

以下情况不会触发类的初始化:

  1. 同类子类引用父类的静态字段,不会导致子类初始化。至于是否会触发子类的加载和验证,取决于虚拟机的具体实现;
  2. 通过数组定义来引用类,也不会触发类的初始化;例如:People[] ps = new People[100];
  3. 引用一个类的常量也不会触发类的初始化

类的加载顺序

什么时候类加载:第一次需要使用类信息时加载。

类加载的原则:延迟加载,能不加载就不加载。

1、加载静态成员/代码块: 先递归地加载父类的静态成员/代码块(Object的最先);再依次加载到本类的静态成员。

同一个类里的静态成员/代码块,按写代码的顺序加载。

如果其间调用静态方法,则调用时会先运行静态方法,再继续加载。同一个类里调用静态方法时,可以不理会写代码的顺序。

调用父类的静态成员,可以像调用自己的一样;但调用其子类的静态成员,必须使用“子类名.成员名”来调用。

2、加载非静态成员/代码块:(实例块在创建对象时才会被加载。而静态成员在不创建对象时可以加载)

先递归地加载父类的非静态成员/代码块(Object的最先);再依次加载到本类的非静态成员。

同一个类里的非静态成员/代码块,按写代码的顺序加载。同一个类里调用方法时,可以不理会写代码的顺序。

但调用属性时,必须注意加载顺序。一般编译不通过,如果能在加载前调用,值为默认初始值(如:null 或者 0)。 调用父类的非静态成员(private

除外),也可以像调用自己的一样。

3、调用构造方法:先递归地调用父类的构造方法(Object的最先)也就是上溯下行;默认调用父类空参的,也可在第一行写明调用父类某个带参的。再依次到本类的构造方法;构造方法内,也可在第一行写明调用某个本类其它的构造方法。 注意:如果加载时遇到 override

的成员,可看作是所需创建的类型赋值给当前类型。

其调用按多态用法:只有非静态方法有多态;而静态方法、静态属性、非静态属性都没有多态。

假设子类override父类的所有成员,包括静态成员、非静态属性和非静态方法。

由于构造子类时会先构造父类;而构造父类时,其所用的静态成员和非静态属性是父类的,但非静态方法却是子类的;

由于构造父类时,子类并未加载;如果此时所调用的非静态方法里有成员,则这个成员是子类的,且非静态属性是默认初始值的。

5类加载时静态代码块一定会执行吗?

答:

静态代码块,在类加载时可以执行,但不一定会执行

通过ClassLoader对象的loadClass方法加载类不会执行静态代码块。

下面列举不同的类的加载方式,静态代码块的执行状况:

class ClassA{

//静态代码块,在类加载时可以执行,但不一定会执行

     static {

           System.out.println("static{}");

     }

     }

//-XX:+TraceClassLoading

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青木编码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值