JVM工作原理与实战(四十三):JVM常见题目

简介: JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了JVM常见题目等内容。

一、JVM常见面试题目

1.什么是类加载器,有哪些常见的类加载器?

类加载器是Java虚拟机(JVM)的一部分,它的主要任务是在类的加载过程中,从文件系统、网络或其他来源动态加载类的字节码,并将其转换为可以在JVM中运行的类。在JDK 9及之后的版本中,类加载器完全由Java实现。

image.gif

常见的类加载器有以下几种:

  • 启动类加载器(Bootstrap ClassLoader):这是JVM的顶层加载类,主要负责加载核心类库,如java.lang、java.util等。这些类库是JVM启动时必须加载的。
  • 平台类加载器(Platform ClassLoader):在JDK 9及之后的版本中,原本的扩展类加载器(Extension ClassLoader)被重命名为平台类加载器。它主要负责加载平台扩展类库,这些类库通常位于$JAVA_HOME/lib/ext目录下。
  • 应用程序类加载器(Application ClassLoader):这个类加载器负责加载应用程序classpath下的类文件。在大多数情况下,它是开发人员与类加载器交互的入口。
  • 自定义类加载器:除了上述三种默认的类加载器,Java还允许开发者根据需要自定义类加载器。自定义类加载器通常通过继承java.lang.ClassLoader类并重写其findClass方法来实现。这样,开发者可以根据自己的需求,从特定的位置(如数据库、网络等)加载类。

参考回答:类加载器是JVM的一部分,负责加载类的字节码到内存中。常见的类加载器有启动类加载器、平台类加载器(在JDK 9+中替代了扩展类加载器)、应用程序类加载器和自定义类加载器。

2.什么是双亲委派机制,以及如何打破双亲委派机制?

双亲委派模型是Java类加载机制中的一个核心概念,它确保了类加载的安全性和一致性。在双亲委派模型中,当一个类加载器收到类加载请求时,它不会自己首先尝试加载,而是将这个请求委派给它的父类加载器去完成。只有当父类加载器无法完成加载请求时,子类加载器才会尝试自己加载。

image.gif

双亲委派模型的主要作用有两点:首先,它避免了类的重复加载,每个类只会被加载一次。其次,它确保了Java核心API的稳定性,因为自定义的类加载器无法加载Java核心类,从而避免了可能的代码冲突和安全风险。

在Java中,可以通过继承ClassLoader并重写其loadClass方法来创建自定义类加载器。通过这种方式,可以打破双亲委派机制,实现类的隔离。例如,在Tomcat中,每个Web应用都有自己的类加载器,从而实现了应用之间的类隔离。当两个Web应用中有相同限定名的类时,如Servlet类,Tomcat通过自定义类加载器保证它们是不同的类。

参考回答:双亲委派模型是Java类加载机制的核心组成部分,它要求一个类加载器在尝试加载一个类之前,先将其加载请求委派给其父类加载器。通过这种方式,从顶层启动类加载器开始,逐级向下进行类加载,确保了核心类库的稳定性与安全性,同时避免了类的重复加载。要打破双亲委派机制,常见的做法是实现自定义类加载器,并重写defineClass方法。

3.如何判断堆上的对象没有被引用?

在Java中,判断堆上对象是否已被垃圾回收的过程是通过可达性分析算法来完成的。这个算法将对象分为两类:垃圾回收的根对象(GC Root)和普通对象。可达性分析算法的核心思想是:如果一个对象从GC Root开始,按照引用关系向下搜索,不可达(即不存在引用链)到该对象,那么该对象就被认为是不可达的,即可以被垃圾回收。最常见的是,GC Root对象会引用栈上的局部变量和静态变量,如果这些引用被断开,那么对应的对象就会变得不可达,从而可能被垃圾回收。与引用计数法相比,可达性分析算法能够更准确地判断对象的可回收性,因为它能够处理循环引用的情况。

引用计数法会为每个对象维护一个引用计数器,每当对象被引用时计数器加1,取消引用时减1。然而,当存在循环引用时,即使对象之间不再需要相互引用,引用计数器也不会归零,从而导致内存泄漏。因此,Java选择了可达性分析算法来管理内存,以避免这类问题。

参考回答:在Java中,通过可达性分析算法判断堆上对象是否可回收。该算法从GC Root开始,检查对象是否可达。若对象不可达,则可能被回收。与引用计数法不同,可达性分析能处理循环引用问题,避免了内存泄漏。因此,Java采用可达性分析算法来管理内存。

4.JVM 中都有哪些引用类型?

在JVM中,存在多种引用类型,每种类型都有其特定的用途和特性。

  • 强引用:JVM中最常见的引用类型,它表示对象被局部变量、静态变量等GC Root所关联。只要强引用存在,垃圾回收器就不会回收该对象。
  • 软引用:一种相对较弱的引用关系。当系统内存足够时,软引用对象不会被回收;但在内存不足时,软引用对象会被回收,以释放内存空间。软引用在缓存框架中特别有用,因为它们允许系统在需要时释放不常使用的数据。
  • 弱引用:与软引用类似,但弱引用的对象在垃圾回收时,无论内存是否充足,都会被回收。弱引用主要在ThreadLocal中使用,以确保线程局部变量的及时清理。
  • 虚引用(也被称为幽灵引用或幻影引用):不会直接关联到对象。它的唯一用途是能在对象被垃圾回收时接收到通知。虚引用主要用于跟踪对象被垃圾回收的活动,例如,在直接内存管理中,虚引用可以用来监控和回收不再使用的内存对象。
  • 终结器引用:涉及对象的finalize方法。当对象被垃圾回收器标记为需要回收时,终结器引用会将其放入Finalizer类的引用队列中。稍后,由FinalizerThread线程从队列中取出对象,并执行其finalize方法。然而,需要注意的是,依赖finalize方法进行资源清理是不推荐的,因为它不是确定性的,并且可能导致性能问题。

参考回答:在JVM中,有四种主要引用类型:强引用、软引用、弱引用和虚引用。强引用是最常见的,它确保对象不被垃圾回收。软引用和弱引用用于内存管理,软引用在内存不足时回收对象,而弱引用则无论内存是否充足都会回收对象。虚引用主要用于对象被回收时的通知。此外,还有终结器引用,它关联对象并执行finalize方法,但现代Java开发中较少使用。

5.ThreadLocal中为什么要使用弱引用?

在ThreadLocal中使用弱引用的主要目的是为了解决内存泄漏问题。通常,ThreadLocal实例作为静态成员变量存在,其生命周期与应用程序的运行时长相同。如果ThreadLocal中存储的对象使用强引用,那么这些对象将一直存在,直到ThreadLocal实例被显式地回收,这可能导致内存泄漏。

通过使用弱引用,当没有其他强引用指向ThreadLocal中的对象时,这些对象可以被垃圾回收器回收,从而减少了内存泄漏的风险。然而,仅仅使用弱引用并不足以完全解决对象回收的问题。因为ThreadLocal内部使用Entry对象来存储值,这些Entry对象本身持有对值的强引用,这意味着即使值对象被弱引用,Entry对象仍然阻止其被回收。

因此,为了避免潜在的内存泄漏,最佳实践是在不再需要ThreadLocal变量时,手动调用其remove()方法来清除条目。这确保了Entry对象被移除,从而允许值对象(如果只有弱引用指向它)被垃圾回收。之后,当解除了对ThreadLocal实例的所有强引用时,该实例本身也可以被回收,从而彻底释放了与其关联的资源。

参考回答:在ThreadLocal中使用弱引用是为了避免内存泄漏。弱引用允许对象在没有其他强引用时被垃圾回收,减少泄漏风险。但仅使用弱引用不足以完全解决回收问题,因为ThreadLocal内部的Entry对象持有强引用。因此,最佳实践是手动调用remove()来清除条目,确保对象可回收。这样,当ThreadLocal不再使用时,相关资源可以被彻底释放。

6.有哪些常见的垃圾回收算法?

常见的垃圾回收算法包括:

  • 标记-清除算法(Mark-Sweep GC)
  • 优点:实现相对简单,能够处理任意对象的回收。
  • 缺点
  • 碎片化问题:由于回收过程中对象的移动和删除,可能导致内存碎片化,影响内存分配效率。
  • 分配速度慢:在清除阶段,需要遍历整个堆来寻找空闲内存,导致内存分配速度较慢。
  • 复制算法(Copying GC)
  • 优点:分配速度快,因为每次只使用一半的内存空间进行分配,没有内存碎片问题。
  • 缺点:内存使用效率低,因为每次只能使用一半的内存空间,限制了可用内存的范围。
  • 标记-整理算法(Mark-Compact GC)
  • 优点:避免了内存碎片问题,通过移动对象来整理内存,使得内存空间连续。
  • 缺点:整理阶段需要高效的算法来移动对象,否则可能导致效率不高。
  • 分代GC(Generational GC)
  • 优点:根据对象的生命周期将内存划分为不同的代,针对不同代使用不同的垃圾回收算法,提高了垃圾回收的效率和灵活性。
  • 缺点:实现复杂度较高,需要针对年轻代和老年代分别设计合适的垃圾回收策略。

在实际应用中,不同的垃圾回收器可能会结合使用上述算法,以适应不同的应用场景和性能需求。例如,在Java的HotSpot虚拟机中,就采用了分代GC的策略,其中年轻代通常使用复制算法,而老年代则使用标记-清除或标记-整理算法。

垃圾回收算法包括标记-清除(简单但可能导致内存碎片和分配速度慢)、复制(分配速度快但内存效率低)、标记-整理(避免碎片但整理可能效率不高)以及分代GC(灵活结合不同算法以满足性能需求)。


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了JVM常见面试题目等内容,希望对大家有所帮助。

目录
打赏
0
1
1
0
45
分享
相关文章
JVM深入原理(一+二):JVM概述和JVM功能
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行。
73 0
|
2月前
|
JVM深入原理(三+四):JVM组成和JVM字节码文件
目录3. JVM组成3.1. 组成-运行时数据区3.2. 组成-类加载器3.3. 组成-执行引擎3.4. 组成-本地接口4. JVM字节码文件4.1. 字节码文件-组成4.1.1. 组成-基础信息4.1.1.1. 基础信息-魔数4.1.1.2. 基础信息-主副版本号4.1.2. 组成-常量池4.1.3. 组成-方法4.1.3.1. 方法-工作流程4.1.4. 组成-字段4.1.5. 组成-属性4.2. 字节码文件-查看工具4.2.1. javap4.2.2. jclasslib4.2.3. 阿里Arthas
46 0
|
2月前
|
JVM深入原理(五):JVM组成和JVM字节码文件
类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析。
37 0
JVM深入原理(六)(一):JVM类加载器
目录6. JVM类加载器6.1. 类加载器-概述6.2. 类加载器-执行流程6.3. 类加载器-分类(JDK8)6.3.1. JVM底层实现的类加载器6.3.1.1. 启动类加载器6.3.2. Java代码实现类的加载器6.3.2.1. 扩展类加载器6.3.2.2. 应用程序类加载器6.4. 类加载器-Arthas查看类加载器
36 0
JVM深入原理(六)(二):双亲委派机制
自定义类加载器打破双亲委派机制的方法:复写ClassLoader中的loadClass方法常见问题:要加载的类名如果是以java.开头,则会抛出安全性异常加载自定义的类都会有一个共同的父类Object,需要在代码中交由父类加载器去加载自定义类加载器不手动指定parent会默认指定应用类加载两个自定义类加载器加载同一个类会被认为是两个对象,只有相同的类加载器+想通的类限定名才会被认为是一个对象。
49 0
|
2月前
|
JVM深入原理(七)(一):运行时数据区
栈的介绍:Java虚拟机栈采用栈的数据结构来管理方法调用中的基本数据,先进后出,每一个方法的调用使用一个栈帧来保存栈的组成:栈:一个线程运行所需要的内存空间,一个栈由多个栈帧组成栈帧:一个方法运行所需要的内存空间活动栈帧:一个线程中只能有一个活动栈帧栈的生命周期:栈随着线程的创建而创建,而回收会在线程销毁时进行栈的执行流程:栈帧压入栈内执行方法执行完毕释放内存若方法间存在调用,那么会压入被调用方法入栈,执行完后释放内存,再执行当前方法,直到执行完毕,释放所有内存。
35 0
|
2月前
|
JVM深入原理(七)(二):运行时数据区
堆的作用:存放对象的内存空间,它是空间最大的一块内存区域.栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享。堆的特点:线程共享:堆中的对象都需要考虑线程安全的问题垃圾回收:堆有垃圾回收机制,不再引用的对象就会被回收方法区的概述:方法区是存放基础信息的位置,线程共享,主要包括:类的元信息:保存了所有类的基本信息运行时常量池:保存了字节码文件中的常量池内容静态常量池:字节码文件通过编号查表的方式找到常量。
40 0
|
2月前
|
JVM深入原理(八)(一):垃圾回收
弱引用-作用:JVM中使用WeakReference对象来实现软引用,一般在ThreadLocal中,当进行垃圾回收时,被弱引用对象引用的对象就直接被回收.软引用-作用:JVM中使用SoftReference对象来实现软引用,一般在缓存中使用,当程序内存不足时,被引用的对象就会被回收.强引用-作用:可达性算法描述的根对象引用普通对象的引用,指的就是强引用,只要有这层关系存在,被引用的对象就会不被垃圾回收。引用计数法-缺点:如果两个对象循环引用,而又没有其他的对象来引用它们,这样就造成垃圾堆积。
68 0
JVM深入原理(八)(二):垃圾回收
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为StopTheWorld简称STW,如果STW时间过长则会影响用户的使用。一般来说,堆内存越大,最大STW就越长,想减少最大STW,就会减少吞吐量,不同的GC算法适用于不同的场景。分代回收算法将整个堆中的区域划分为新生代和老年代。--超过新生代大小的大对象会直接晋升到老年代。
62 0
JVM实战—13.OOM的生产案例
本文详细探讨了多种线上系统中引发OOM(内存溢出)问题的原因及排查方法。内容涵盖:1)每秒仅上百请求的系统因RPC超时时间设置过长导致QPS激增而OOM;2)Jetty服务器NIO机制因堆外内存管理不当引发内存溢出;3)微服务架构下RPC调用因类定义不一致导致超大byte[]数组占用内存;4)SQL语句缺少WHERE条件查询大量数据引发OOM;5)日志分析系统因堆内存不足与递归操作耗尽内存;6)类加载器过多导致内存使用过高被OS杀死进程;7)数据同步系统频繁OOM的排查与解决;8)总结JVM参数优化、GC问题定位及OOM分析方法。
JVM实战—13.OOM的生产案例
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问