美文网首页Java高级架构Java架构技术栈程序员
精选20道JVM面试重点常问问题及学习思路分享(含答案)!

精选20道JVM面试重点常问问题及学习思路分享(含答案)!

作者: 若丨寒 | 来源:发表于2019-04-22 20:19 被阅读19次

    JVM 是小白 Java 程序员成长路上的一道坎也是很多工作2到3年程序员的一个重点面试问题之一,学习JVM之前,只需要知道代码编译成 Class,虚拟机加载 Class 运行就可以了,学习 JVM 之后,可以深入理解代码从编译到加载的过程、内存中对象的创建与垃圾回收、日常开发中快速定位性能问题等,当然也是面试不可缺少的加分项。在这我整理20道精选的JVM面试题分享给大家,希望能帮助到大家同时可以给此文点个喜欢,后续会更新Java面试常问的一些问题,有兴趣的朋友可以持续关注下我,多谢支持!

    面试问题如下(文末有我的一些JVM学习笔记):

    1.Java 类加载过程?

    答:Java 类加载需要经历一下 7 个过程:

    1. 加载

    加载是类加载的第一个过程,在这个阶段,将完成一下三件事情:

    • 通过一个类的全限定名获取该类的二进制流。
    • 将该二进制流中的静态存储结构转化为方法去运行时数据结构。
    • 在内存中生成该类的 Class 对象,作为该类的数据访问入口。

    2. 验证

    验证的目的是为了确保 Class 文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证:

    • 文件格式验证:验证字节流是否符合 Class 文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型.
    • 元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。
    • 字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。
    • 符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。

    3. 准备

    准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。

    public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 
    

    4. 解析

    该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。

    5. 初始化

    初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的 Java 程序代码。

    6. 使用

    7. 卸载

    2.描述一下 JVM 加载 Class 文件的原理机制?

    答:Java 语言是一种具有动态性的解释型语言,类(Class)只有被加载到 JVM 后才能运行。当运行指定程序时,JVM 会将编译生成的 .class 文件按照需求和一定的规则加载到内存中,并组织成为一个完整的 Java 应用程序。这个加载过程是由类加载器完成,具体来说,就是由 ClassLoader 和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。

    类的加载方式分为隐式加载和显示加载。隐式加载指的是程序在使用 new 等方式创建对象时,会隐式地调用类的加载器把对应的类加载到 JVM 中。显示加载指的是通过直接调用 class.forName() 方法来把所需的类加载到 JVM 中。

    任何一个工程项目都是由许多类组成的,当程序启动时,只把需要的类加载到 JVM 中,其他类只有被使用到的时候才会被加载,采用这种方法一方面可以加快加载速度,另一方面可以节约程序运行时对内存的开销。此外,在 Java 语言中,每个类或接口都对应一个 .class 文件,这些文件可以被看成是一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。

    在 Java 语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(例如基类)完全加载到 JVM 中,至于其他类,则在需要的时候才加载。

    类加载的主要步骤:

    • 装载,根据查找路径找到相应的 class 文件,然后导入。
    • 链接,链接又可分为 3 个小步:
    • 检查,检查待加载的 class 文件的正确性。
    • 准备,给类中的静态变量分配存储空间。
    • 解析,将符号引用转换为直接引用(这一步可选)
    • 初始化。对静态变量和静态代码块执行初始化工作。

    3. Java 内存分配。

    • 寄存器:我们无法控制。
    • 静态域:static定义的静态成员。
    • 常量池:编译时被确定并保存在 .class 文件中的(final)常量值和一些文本修饰的符号引用(类和接口的全限定名,字段的名称和描述符,方法和名称和描述符)。
    • 非 RAM 存储:硬盘等永久存储空间。
    • 堆内存:new 创建的对象和数组,由 Java 虚拟机自动垃圾回收器管理,存取速度慢。
    • 栈内存:基本类型的变量和对象的引用变量(堆内存空间的访问地址),速度快,可以共享,但是大小与生存期必须确定,缺乏灵活性。

    4.Java 堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

    答:JVM 的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在 JVM 启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。

    堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些 对象回收掉之前,他们会一直占据堆内存空间。

    5.GC 是什么? 为什么要有 GC?

    答:GC 是垃圾收集的意思(GabageCollection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java 语言没有提供释放已分配内存的显示操作方法。

    6.简述 Java 垃圾回收机制?

    答:在 Java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在 JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。

    7. 如何判断一个对象是否存活?(或者 GC 对象的判定方法)

    答:判断一个对象是否存活有两种方法:

    1. 引用计数法

    所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收.

    引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象 B 又引用者对象 A,那么此时 A、B 对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。

    2. 可达性算法(引用链法)

    该算法的思想是:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则说明此对象不可用。

    在 Java 中可以作为 GC Roots 的对象有以下几种:

    • 虚拟机栈中引用的对象
    • 方法区类静态属性引用的对象
    • 方法区常量池引用的对象
    • 本地方法栈JNI引用的对象

    虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比不一定会被回收。当一个对象不可达 GC Root 时,这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记.

    如果对象在可达性分析中没有与 GC Root 的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行 finalize() 方法。当对象没有覆盖 finalize() 方法或者已被虚拟机调用过,那么就认为是没必要的。 如果该对象有必要执行 finalize() 方法,那么这个对象将会放在一个称为 F-Queue 的对队列中,虚拟机会触发一个 Finalize() 线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果 finalize() 执行缓慢或者发生了死锁,那么就会造成 F-Queue 队列一直等待,造成了内存回收系统的崩溃。GC 对处于 F-Queue 中的对象进行第二次被标记,这时,该对象将被移除” 即将回收” 集合,等待回收。

    8.垃圾回收的优点和原理。并考虑 2 种回收机制?

    答:Java 语言中一个显著的特点就是引入了垃圾回收机制,使 C++ 程序员最头疼的内存管理的问题迎刃而解,它使得 Java 程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java 中的对象不再有“作用域”的概念,只有对象的引用才有"作用域"。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。

    回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。

    9.什么是分布式垃圾回收(DGC)?它是如何工作的?

    答:DGC 叫做分布式垃圾回收。RMI 使用 DGC 来做自动垃圾回收。因为 RMI 包含了跨虚拟机的远程对象的引用,垃圾回收是很困难的。DGC 使用引用计数算法来给远程对象提供自动内存管理。
    10.在 Java 中,对象什么时候可以被垃圾回收?

    答:当对象对当前使用这个对象的应用程序变得不可触及的时候,这个对象就可以被回收了。

    11.简述 Java 内存分配与回收策率以及 Minor GC 和 Major GC。

    • 对象优先在堆的 Eden 区分配
    • 大对象直接进入老年代
    • 长期存活的对象将直接进入老年代

    当 Eden 区没有足够的空间进行分配时,虚拟机会执行一次 Minor GC。Minor GC 通常发生在新生代的 Eden 区,在这个区的对象生存期短,往往发生 Gc 的频率较高,回收速度比较快;Full GC/Major GC 发生在老年代,一般情况下,触发老年代 GC 的时候不会触发 Minor GC,但是通过配置,可以在 Full GC 之前进行一次 Minor GC 这样可以加快老年代的回收速度。

    12.串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?

    答:吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。 而串行收集器对大多数的小应用(在现代处理器上需要大概 100M 左右的内存)就足够了。

    13.Java 中垃圾收集的方法有哪些?

    可以参考之前我发过的一篇文章JVM系列(四):浅谈常用四种垃圾回收算法总结

    14.什么是类加载器,类加载器有哪些?

    答:实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。

    主要有一下四种类加载器:

    • 启动类加载器(Bootstrap ClassLoader)用来加载 Java 核心类库,无法被 Java 程序直接引用。
    • 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
    • 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader() 来获取它。
    • 用户自定义类加载器,通过继承 java.lang.ClassLoader 类的方式实现。

    15.类加载器双亲委派模型机制?

    可以参考之前我发过的一篇文章JVM系列(九):Java类加载机制之双亲委派模型

    16.垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?

    答:对于 GC 来说,当程序员创建对象时,GC 就开始监控这个对象的地址、大小以及使用情况。通常,GC 采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当 GC 确定一些对象为“不可达”时,GC 就有责任回收这些内存空间。可以。程序员可以手动执行 System.gc(),通知 GC 运行,但是 Java 语言规范并不保证 GC 一定会执行。

    17.System.gc() 和 Runtime.gc() 会做什么事情?

    答:这两个方法用来提示 JVM 要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于 JVM 的。

    18.finalize() 方法什么时候被调用?析构函数 (finalization) 的目的是什么?

    答:垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的 finalize() 方法 但是在 Java 中很不幸,如果内存总是充足的,那么垃圾回收可能永远不会进行,也就是说 filalize() 可能永远不被执行,显然指望它做收尾工作是靠不住的。 那么 finalize() 究竟是做什么的呢? 它最主要的用途是回收特殊渠道申请的内存。Java 程序有垃圾回收器,所以一般情况下内存问题不用程序员操心。但有一种 JNI(Java Native Interface)调用 non-­Java 程序(C 或 C++), finalize() 的工作就是回收这部分的内存。

    19.Java 中会存在内存泄漏吗,请简单描述。

    此题我后面会单独写篇文章,有兴趣的朋友可以关注下和点个喜欢,谢谢支持!

    20.如何判断对象是否已死
    可以参考之前我发过的一篇文章JVM系列(三):JVM的判断对象是否已死分析总结!

    JVM与性能优化学习导图笔记:

    (此Xmind图是平时学习时做的一些笔记和重点缩略未展开)

    知识笔记导图

    读者分享

    在这给大家推荐一个微信公众号,那里每天都会有技术干货、技术动向、职业生涯、行业热点、职场趣事等一切有关于程序员的内容分享。更有海量Java架构、移动互联网架构相关源码视频,面试资料,电子书籍截止于4月28日免费发放。我看了觉得资源还不错,如果你们有需要的话,扫描下方二维码关注wx公众号免费获取↓↓↓


    资源大本营↓↓↓

    Java架构资料
    Java源码解析,到各种框架学习,再到项目实战,一应俱全,包括但不限于:Spring、Mybatis等源码、Java进阶、Java架构师、虚拟机、性能优化、并发编程、数据结构和算法。

    Java架构视频资料
    Java架构视频资料
    java架构面试专题资料

    移动互联网架构资料
    Android进阶、Android架构、APP开发、NDK模块开发、小程序开发、微信开发。

    Android架构资料

    相关文章

      网友评论

        本文标题:精选20道JVM面试重点常问问题及学习思路分享(含答案)!

        本文链接:https://www.haomeiwen.com/subject/jsaugqtx.html