为您推荐:
精华内容
最热下载
问答
  • 5星
    15.9MB Yao__Shun__Yu 2021-01-26 09:52:27
  • 最新Jvm面试题及答案【附答案解析】Jvm面试题大全带答案,Jvm面试题最新面试题,Jvm面试题新答案已经全部更新完了,有些答案是自己总结的,也有些Jvm面试题答案是在网上搜集整理的。这些答案难免会存在一些错误,仅...

    最新Jvm面试题及答案【附答案解析】Jvm面试题大全带答案,Jvm面试题最新面试题,Jvm面试题新答案已经全部更新完了,有些答案是自己总结的,也有些Jvm面试题答案是在网上搜集整理的。这些答案难免会存在一些错误,仅供大家参考。如果发现错误还望大家多多包涵,不吝赐教,谢谢~

    如果不背 Jvm面试题的答案,肯定面试会挂!

    这套Jvm面试题大全,希望对大家有帮助哈~

    博主已将以下这些面试题整理成了一个面试手册,是PDF版的

    1、如何开启和查看 GC 日志?

    常见的 GC 日志开启参数包括:

    1、 -Xloggc:filename,指定日志文件路径

    2、 -XX:+PrintGC,打印 GC 基本信息

    3、 -XX:+PrintGCDetails,打印 GC 详细信息

    4、 -XX:+PrintGCTimeStamps,打印 GC 时间戳

    5、 -XX:+PrintGCDateStamps,打印 GC 日期与时间

    6、 -XX:+PrintHeapAtGC,打印 GC 前后的堆、方法区、元空间可用容量变化

    7、 -XX:+PrintTenuringDistribution,打印熬过收集后剩余对象的年龄分布信息,有助于 MaxTenuringThreshold 参数调优设置

    8、 -XX:+PrintAdaptiveSizePolicy,打印收集器自动设置堆空间各分代区域大小、收集目标等自动调节的相关信息

    9、 -XX:+PrintGCApplicationConcurrentTime,打印 GC 过程中用户线程并发时间

    10、 -XX:+PrintGCApplicationStoppedTime,打印 GC 过程中用户线程停顿时间

    11、 -XX:+HeapDumpOnOutOfMemoryError,堆 oom 时自动 dump

    12、 -XX:HeapDumpPath,堆 oom 时 dump 文件路径

    Java 9 JVM 日志模块进行了重构,参数格式发生变化,这个需要知道。

    GC 日志输出的格式,会随着上面的参数不同而发生变化。关注各个分代的内存使用情况、垃圾回收次数、垃圾回收的原因、垃圾回收占用的时间、吞吐量、用户线程停顿时间。

    借助工具可视化工具可以更方便的分析,在线工具 GCeasy;离线版可以使用 GCViewer。

    如果现场环境不允许,可以使用 JDK 自带的 jstat 工具监控观察 GC 情况。

    2、Parallel Scavenge 收集器(多线程复制算法、高效)

    Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃圾收集器, 它重点关注的是程序达到一个可控制的吞吐量(Thoughput, CPU 用于运行用户代码的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个重要区别。

    3、说下有哪些类加载器?

    Bootstrap ClassLoader(启动类加载器) Extention ClassLoader(扩展类加载器) App ClassLoader(应用类加载器)

    4、你做过 JVM 调优,说说如何查看 JVM 参数默认值?

    1、 jps -v 可以查看 jvm 进程显示指定的参数

    2、 使用 -XX:+PrintFlagsFinal 可以看到 JVM 所有参数的值

    3、 jinfo 可以实时查看和调整虚拟机各项参数

    5、什么是双亲委派机制?

    双亲委派机制的意思是除了顶层的启动类加载器以外,其余的类加载器,在加载之前,都会委派给它的父加载器进行加载。这样一层层向上传递,直到祖先们都无法胜任,它才会真正的加载。

    6、内存溢出和内存泄漏的区别?

    内存溢出 OutOfMemory,指程序在申请内存时,没有足够的内存空间供其使用。

    内存泄露 Memory Leak,指程序在申请内存后,无法释放已申请的内存空间,内存泄漏最终将导致内存溢出。

    7、强引用、软引用、弱引用、虚引用是什么,有什么区别?

    1、 强引用,就是普通的对象引用关系,如 String s = new String("ConstXiong")

    2、 软引用,用于维护一些可有可无的对象。只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。SoftReference 实现

    3、 弱引用,相比软引用来说,要更加无用一些,它拥有更短的生命周期,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。WeakReference 实现

    4、 虚引用是一种形同虚设的引用,在现实场景中用的不是很多,它主要用来跟踪对象被垃圾回收的活动。PhantomReference 实现

    8、垃圾回收的优点和原理。说说2种回收机制

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

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

    9、说一下垃圾分代收集的过程

    分为新生代和老年代,新生代默认占总空间的 1/3,老年代默认占 2/3。

    新生代使用复制算法,有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1。

    当新生代中的 Eden 区内存不足时,就会触发 Minor GC,过程如下:

    1、 在 Eden 区执行了第一次 GC 之后,存活的对象会被移动到其中一个 Survivor 分区;

    2、 Eden 区再次 GC,这时会采用复制算法,将 Eden 和 from 区一起清理,存活的对象会被复制到 to 区;

    3、 移动一次,对象年龄加 1,对象年龄大于一定阀值会直接移动到老年代

    4、 Survivor 区相同年龄所有对象大小的总和 (Survivor 区内存大小 * 这个目标使用率)时,大于或等于该年龄的对象直接进入老年代。其中这个使用率通过 -XX:TargetSurvivorRatio 指定,默认为 50%

    5、 Survivor 区内存不足会发生担保分配

    6、 超过指定大小的对象可以直接进入老年代

    Major GC,指的是老年代的垃圾清理,但并未找到明确说明何时在进行Major GC

    FullGC,整个堆的垃圾收集,触发条件:

    1、 每次晋升到老年代的对象平均大小>老年代剩余空间

    2、 MinorGC后存活的对象超过了老年代剩余空间

    3、 元空间不足

    4、 System.gc() 可能会引起

    5、 CMS GC异常,promotion failed:MinorGC时,survivor空间放不下,对象只能放入老年代,而老年代也放不下造成;concurrent mode failure:GC时,同时有对象要放入老年代,而老年代空间不足造成

    6、 堆内存分配很大的对象

    10、JVM 运行时内存

    Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、 From Survivor 区和 To Survivor 区)和老年代。

    新生代

    是用来存放新生的对象。一般占据堆的 1/3 空间。由于频繁创建对象,所以新生代会频繁触发MinorGC 进行垃圾回收。新生代又分为 Eden区、 ServivorFrom、 ServivorTo 三个区。

    Eden 区

    Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当 Eden 区内存不够的时候就会触发 MinorGC,对新生代区进行一次垃圾回收。

    ServivorFrom

    上一次 GC 的幸存者,作为这一次 GC 的被扫描者。

    ServivorTo

    保留了一次 MinorGC 过程中的幸存者。

    MinorGC 的过程(复制->清空->互换)

    MinorGC 采用复制算法。

    eden、 servicorFrom 复制到 ServicorTo,年龄+1

    首先,把 Eden 和 ServivorFrom 区域中存活的对象复制到 ServicorTo 区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果 ServicorTo 不够位置了就放到老年区);

    清空 eden、 servicorFrom

    然后,清空 Eden 和 ServicorFrom 中的对象;

    ServicorTo 和 ServicorFrom 互换

    最后, ServicorTo 和 ServicorFrom 互换,原 ServicorTo 成为下一次 GC 时的 ServicorFrom区。


    11、GC Roots 有哪些?

    1、 GC Roots 是一组必须活跃的引用。用通俗的话来说,就是程序接下来通过直接引用或者间接引用,能够访问到的潜在被使用的对象。

    2、 GC Roots 包括:Java 线程中,当前所有正在被调用的方法的引用类型参数、局部变量、临时值等。也就是与我们栈帧相关的各种引用。所有当前被加载的 Java 类。Java 类的引用类型静态变量。运行时常量池里的引用类型常量(String 或 Class 类型)。JVM 内部数据结构的一些引用,比如 sun.jvm.hotspot.memory.Universe 类。用于同步的监控对象,比如调用了对象的 wait() 方法。JNI handles,包括 global handles 和 local handles。

    3、 这些 GC Roots 大体可以分为三大类,下面这种说法更加好记一些:活动线程相关的各种引用。类的静态变量的引用。JNI 引用。

    4、 有两个注意点:我们这里说的是活跃的引用,而不是对象,对象是不能作为 GC Roots 的。GC 过程是找出所有活对象,并把其余空间认定为“无用”;而不是找出所有死掉的对象,并回收它们占用的空间。所以,哪怕 JVM 的堆非常的大,基于 tracing 的 GC 方式,回收速度也会非常快。

    12、说说Java 垃圾回收机制

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

    13、介绍一下类文件结构吧!

    魔数: 确定这个文件是否为一个能被虚拟机接收的 Class 文件。Class 文件版本 :Class 文件的版本号,保证编译正常执行。常量池 :常量池主要存放两大常量:字面量和符号引用。访问标志 :标志用于识别一些类或者接口层次的访问信息,包括:这个 Class 是类还是接口,是否为 public 或者 abstract 类型,如果是类的话是否声明为 final 等等。当前类索引,父类索引 :类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,由于 Java 语言的单继承,所以父类索引只有一个,除了 java.lang.Object 之外,所有的 java 类都有父类,因此除了 java.lang.Object 外,所有 Java 类的父类索引都不为 0。接口索引集合 :接口索引集合用来描述这个类实现了那些接口,这些被实现的接口将按implents(如果这个类本身是接口的话则是extends) 后的接口顺序从左到右排列在接口索引集合中。字段表集合 :描述接口或类中声明的变量。字段包括类级变量以及实例变量,但不包括在方法内部声明的局部变量。方法表集合 :类中的方法。属性表集合 :在 Class 文件,字段表,方法表中都可以携带自己的属性表集合。

    14、分代收集算法

    当前主流 VM 垃圾收集都采用”分代收集” (Generational Collection)算法, 这种算法会根据对象存活周期的不同将内存划分为几块, 如 JVM 中的新生代、老年代、永久代, 这样就可以根据各年代特点分别采用最适当的 GC 算法

    15、堆溢出的原因?

    堆用于存储对象实例,只要不断创建对象并保证 GC Roots 到对象有可达路径避免垃圾回收,随着对象数量的增加,总容量触及最大堆容量后就会 OOM,例如在 while 死循环中一直 new 创建实例。

    堆 OOM 是实际应用中最常见的 OOM,处理方法是通过内存映像分析工具对 Dump 出的堆转储快照分析,确认内存中导致 OOM 的对象是否必要,分清到底是内存泄漏还是内存溢出。

    如果是内存泄漏,通过工具查看泄漏对象到 GC Roots 的引用链,找到泄露对象是通过怎样的引用路径、与哪些 GC Roots 关联才导致无法回收,一般可以准确定位到产生内存泄漏代码的具***置。

    如果不是内存泄漏,即内存中对象都必须存活,应当检查 JVM 堆参数,与机器内存相比是否还有向上调整的空间。再从代码检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行期的内存消耗。

    16、Tomcat是怎么打破双亲委派机制的呢?

    是通过重写ClassLoader#loadClass和ClassLoader#findClass 实现的。可以看图中的WebAppClassLoader,它加载自己目录下的.class文件,并不会传递给父类的加载器。但是,它却可以使用 SharedClassLoader 所加载的类,实现了共享和分离的功能。

    17、你知道哪些垃圾收集器?

    序列号

    最基础的收集器,使用复制算法、单线程工作,只用一个处理器或一条线程完成垃圾收集,进行垃圾收集时必须暂停其他所有工作线程。

    Serial 是虚拟机在客户端模式的默认新生代收集器,简单高效,对于内存受限的环境它是所有收集器中额外内存消耗最小的,对于处理器核心较少的环境,Serial 由于没有线程交互开销,可获得最高的单线程收集效率。

    新品

    Serial 的多线程版本,除了使用多线程进行垃圾收集外其余行为完全一致。

    ParNew 是虚拟机在服务端模式的默认新生代收集器,一个重要原因是除了 Serial 外只有它能与 CMS 配合。自从 JDK 9 开始,ParNew 加 CMS 不再是官方推荐的解决方案,官方希望它被 G1 取代。

    并行清理

    新生代收集器,基于复制算法,是可并行的多线程收集器,与 ParNew 类似。

    特点是它的关注点与其他收集器不同,Parallel Scavenge 的目标是达到一个可控制的吞吐量,吞吐量就是处理器用于运行用户代码的时间与处理器消耗总时间的比值。

    串行旧

    Serial 的老年代版本,单线程工作,使用标记-整理算法。

    Serial Old 是虚拟机在客户端模式的默认老年代收集器,用于服务端有两种用途:① JDK5 及之前与 Parallel Scavenge 搭配。② 作为CMS 失败预案。

    平行老

    Parallel Scavenge 的老年代版本,支持多线程,基于标记-整理算法。JDK6 提供,注重吞吐量可考虑 Parallel Scavenge 加 Parallel Old。

    不育系

    以获取最短回收停顿时间为目标,基于标记-清除算法,过程相对复杂,分为四个步骤:初始标记、并发标记、重新标记、并发清除。

    初始标记和重新标记需要 STW(Stop The World,系统停顿),初始标记仅是标记 GC Roots 能直接关联的对象,速度很快。并发标记从 GC Roots 的直接关联对象开始遍历整个对象图,耗时较长但不需要停顿用户线程。重新标记则是为了修正并发标记期间因用户程序运作而导致标记产生变动的那部分记录。并发清除清理标记阶段判断的已死亡对象,不需要移动存活对象,该阶段也可与用户线程并发。

    缺点:① 对处理器资源敏感,并发阶段虽然不会导致用户线程暂停,但会降低吞吐量。② 无法处理浮动垃圾,有可能出现并发失败而导致 Full GC。③ 基于标记-清除算法,产生空间碎片。

    G1

    开创了收集器面向局部收集的设计思路和基于 Region 的内存布局,主要面向服务端,最初设计目标是替换 CMS。

    G1 之前的收集器,垃圾收集目标要么是整个新生代,要么是整个老年代或整个堆。而 G1 可面向堆任何部分来组成回收集进行回收,衡量标准不再是分代,而是哪块内存中存放的垃圾数量最多,回收受益最大。

    跟踪各 Region 里垃圾的价值,价值即回收所获空间大小以及回收所需时间的经验值,在后台维护一个优先级列表,每次根据用户设定允许的收集停顿时间优先处理回收价值最大的 Region。这种方式保证了 G1 在有限时间内获取尽可能高的收集效率。

    G1 运作过程:

    初始标记:标记 GC Roots 能直接关联到的对象,让下一阶段用户线程并发运行时能正确地在可用 Region 中分配新对象。需要 STW 但耗时很短,在 Minor GC 时同步完成。

    并发标记:从 GC Roots 开始对堆中对象进行可达性分析,递归扫描整个堆的对象图。耗时长但可与用户线程并发,扫描完成后要重新处理 SATB 记录的在并发时有变动的对象。

    最终标记:对用户线程做短暂暂停,处理并发阶段结束后仍遗留下来的少量 SATB 记录。

    筛选回收:对各 Region 的回收价值排序,根据用户期望停顿时间制定回收计划。必须暂停用户线程,由多条收集线程并行完成。

    可由用户指定期望停顿时间是 G1 的一个强大功能,但该值不能设得太低,一般设置为100~300 ms。

    18、JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用

    当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM主要动机在于可以指定最大堆大小,通过压缩OOP 可以节省一定的内存。通过-XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。

    19、invokedynamic 指令是干什么的?

    Java 7 开始,新引入的字节码指令,可以实现一些动态类型语言的功能。Java 8 的 Lambda 表达式就是通过 invokedynamic 指令实现,使用方法句柄实现。

    20、动态改变构造

    OSGi 服务平台提供在多种网络设备上无需重启的动态改变构造的功能。为了最小化耦合度和促使这些耦合度可管理, OSGi 技术提供一种面向服务的架构,它能使这些组件动态地发现对方。

    21、类加载有几个过程?

    加载、验证、准备、解析、初始化。

    22、简述Java的对象结构

    Java对象由三个部分组成:对象头、实例数据、对齐填充。

    对象头由两部分组成,第一部分存储对象自身的运行时数据:哈希码、GC分代年龄、锁标识状态、线程持有的锁、偏向线程ID(一般占32/64 bit)。第二部分是指针类型,指向对象的类元数据类型(即对象代表哪个类)。如果是数组对象,则对象头中还有一部分用来记录数组长度。

    实例数据用来存储对象真正的有效信息(包括父类继承下来的和自己定义的)

    对齐填充:JVM要求对象起始地址必须是8字节的整数倍(8字节对齐 )

    23、怎么查看服务器默认的垃圾回收器是哪一个?

    这通常会使用另外一个参数:-XX:+PrintCommandLineFlags可以打印所有的参数,包括使用的垃圾回收器。

    24、JAVA 强引用

    在 Java 中最常见的就是强引用, 把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到 JVM 也不会回收。因此强引用是造成 Java 内存泄漏的主要原因之一。

    25、详细介绍一下JVM内存模型

    根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。

    具体可能会聊聊jdk1.7以前的PermGen(永久代),替换成Metaspace(元空间)

    1、 原本永久代存储的数据:符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap

    2、 Metaspace(元空间)存储的是类的元数据信息(metadata)

    3、 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。

    4、 替换的好处:一、字符串存在永久代中,容易出现性能问题和内存溢出。二、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

    26、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?

    理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5GB,Solaris 大约3GB。64 位 JVM 允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。

    27、HashMap中的key,可以是普通对象么?需要什么注意的地方?

    Map的key和value都可以是任何类型。但要注意的是,一定要重写它的equals和hashCode方法,否则容易发生内存泄漏。

    28、你熟悉哪些垃圾收集算法?

    标记清除(缺点是碎片化) 复制算法(缺点是浪费空间) 标记整理算法(效率比前两者差) 分代收集算法(老年代一般使用“标记-清除”、“标记-整理”算法,年轻代一般用复制算法)

    29、GC 垃圾收集器

    Java 堆内存被划分为新生代和年老代两部分,新生代主要使用复制和标记-清除垃圾回收算法;年老代主要使用标记-整理垃圾回收算法,因此 java 虚拟中针对新生代和年老代分别提供了多种不同的垃圾收集器, JDK1.6 中 Sun HotSpot 虚拟机的垃圾收集器

    30、什么情况发生栈溢出?

    -Xss可以设置线程栈的大小,当线程方法递归调用层次太深或者栈帧中的局部变量过多时,会出现栈溢出错误 java.lang.StackOverflowError

    更多Jvm面试题

    01、JVM 数据运行区,哪些会造成 OOM 的情况?

    02、谈谈你知道的垃圾回收算法

    03、Java 内存分配与回收策率以及 Minor GC 和 Major GC

    04、JVM有哪些内存区域?(JVM的内存布局是什么?)

    05、被引用的对象就一定能存活吗?

    06、JVM调优命令有哪些?

    07、说说类加载的过程

    08、Java 程序是怎样运行的?

    09、谈谈对 OOM 的认识

    10、Java 虚拟机栈的作用?

    11、标记整理算法(Mark-Compact)

    12、谈谈JVM中,对类加载器的认识

    13、栈帧都有哪些数据?

    14、64 位 JVM 中,int 的长度是多数?

    15、程序计数器是什么?

    16、Tomcat是怎么打破双亲委派机制的呢?

    17、运行时栈帧包含哪些结构?

    18、GC日志的real、user、sys是什么意思?

    19、什么是程序计数器

    20、说说ZGC垃圾收集器的工作原理

    21、你能保证 GC 执行吗?

    22、动态改变构造

    23、JRE、JDK、JVM 及 JIT 之间有什么不同?

    24、说说类加载的过程

    25、CMS 收集器(多线程标记清除算法)

    26、讲讲什么情况下会出现内存溢出,内存泄漏?

    27、说下有哪些类加载器?

    28、说一下垃圾分代收集的过程

    29、描述一下什么情况下,对象会从年轻代进入老年代

    30、类加载有几个过程?

    31、safepoint是什么?

    32、什么是方法内联?

    33、有哪些打破了双亲委托机制的案例?

    34、java中会存在内存泄漏吗,请简单描述。

    35、有哪些 GC 算法?

    36、如何判断一个类是无用的类?

    37、怎么获取 Java 程序使用的内存?堆使用的百分比?

    38、JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的

    39、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?

    40、Java 内存分配

    41、标记清除算法( Mark-Sweep)

    42、Serial Old 收集器(单线程标记整理算法 )

    43、你都有哪些手段用来排查内存溢出?

    44、类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?

    45、描述一下 JVM 加载 class 文件的原理机制

    46、能够找到 Reference Chain 的对象,就一定会存活么?

    47、类加载器双亲委派模型机制?

    48、字符串常量存放在哪个区域?

    49、你知道哪些内存分配与回收策略?

    50、Java 8 为什么要将永久代(PermGen)替换为元空间(MetaSpace)呢?

    51、谈谈永久代

    52、简述Java的对象结构

    53、SWAP会影响性能么?

    54、列举一些你知道的打破双亲委派机制的例子。为什么要打破?

    55、内存溢出和内存泄漏的区别?

    56、对于JDK自带的监控和性能分析工具用过哪些?

    57、Serial 垃圾收集器(单线程、 复制算法)

    58、GC垃圾回收算法与垃圾收集器的关系?

    59、方法区/永久代(线程共享)

    60、safepoint 是什么?

    如果不背 Jvm面试题的答案,肯定面试会挂!

    这套Jvm面试题大全,希望对大家有帮助哈~

    博主已将以下这些面试题整理成了一个面试手册,是PDF版的

    展开全文
    u012889902 2021-12-13 18:03:08
  • JVM是JAVA虚拟机的英文缩写,主要承担JAVA字节码的执行作用,执行过程中会经历加载class文件→管理并分配...如果近期有面试需求,不妨多刷一刷JVM面试题,知道的多了,面试的时候才有侃侃而谈的资本。整理了上百道比较

    JVM是JAVA虚拟机的英文缩写,主要承担JAVA字节码的执行作用,执行过程中会经历加载class文件→管理并分配内存→执行垃圾回收机制,JAVA程序需要跨平台执行的时候,就需要JVM来实现。

    如果有过几次面试经验或者有圈内朋友的话,应该知道在大厂面试中,基本上或多或少会问到有关于虚拟机的问题。许多经验不足的或者应届生,在应对面试官时只会说:“我知道JVM内存”,当说出这句话的时候,面试已经结束了。

    如果近期有面试需求,不妨多刷一刷JVM面试题,知道的多了,面试的时候才有侃侃而谈的资本。整理了上百道比较常见的JVM面试题,都是圈内人事在面试的时候被问到过的题目,并且答案已经给大家分析总结了,希望对大家有用。


    1.内存模型以及分区,需要详细到每个区放什么。

    JVM分为堆区和栈区,还有方法区,初始化的对象放在堆里面,引用放在栈里面,class类信息常量池(static常量和static变量)等放在方法区。

    new:

    方法区:主要是存储类信息,常量池(static常量和static变量),编译后的代码(字节码)等数据。

    堆:初始化的对象,成员变量(那种非static的变量),所有的对象实例和数组都要在堆上分配。

    栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量表,操作数栈,方法出口等信息,局部变量表存放的是8大基础类型加上一个应用类型,所以还是一个指向地址的指针。

    本地方法栈:主要为Native方法服务程序计数器:记录当前线程执行的行号。

    2.堆里面的分区:Eden,survival(from+to),老年代,各自的特点。

    堆里面分为新生代和老生代(java8取消了永久代,采用了Metaspace),新生代包含Eden+Survivor区,survivor区里面分为from和to区,内存回收时,如果用的是复制算法,从from复制到to,当经过一次或者多次GC之后,存活下来的对象会被移动到老年区,当JVM内存不够用的时候,会触发FullGC,清理JVM老年区当新生区满了之后会触发YGC,先把存活的对象放到其中一个Survice区,然后进行垃圾清理。

    因为如果仅仅清理需要删除的对象,这样会导致内存碎片,因此一般会把Eden进行完全的清理,然后整理内存。那么下次GC的时候,就会使用下一个Survive,这样循环使用。如果有特别大的对象,新生代放不下,就会使用老年代的担保,直接放到老年代里面。因为JVM认为,一般大对象的存活时间一般比较久远。

    3.对象创建方法,对象的内存分配,对象的访问定位。

    new一个对象

    4.GC的两种判定方法

    引用计数法:指的是如果某个地方引用了这个对象就+1,如果失效了就-1,当为0就会回收但是JVM没有用这种方式,因为无法判定相互循环引用(A引用B,B引用A)的情况。

    引用链法:通过一种GCROOT的对象(方法区中静态变量引用的对象等-static变量)来判断,如果有一条链能够到达GCROOT就说明,不能到达GCROOT就说明可以回收。

    5.SafePoint是什么

    比如GC的时候必须要等到Java线程都进入到safepoint的时候VMThread才能开始执行GC。

    1.循环的末尾(防止大循环的时候一直不进入safepoint,而其他线程在等待它进入safepoint)

    2.方法返回前

    3.调用方法的call之后

    4.抛出异常的位置

    6.GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?

    先标记,标记完毕之后再清除,效率不高,会产生碎片。

    复制算法:分为8:1的Eden区和survivor区,就是上面谈到的YGC。

    标记整理:标记完毕之后,让所有存活的对象向一端移动。

    7.GC收集器有哪些?CMS收集器与G1收集器的特点。

    并行收集器:串行收集器使用一个单独的线程进行收集,GC时服务有停顿时间。

    串行收集器:次要回收中使用多线程来执行。

    CMS收集器是基于“标记—清除”算法实现的,经过多次标记才会被清除G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的。

    8.MinorGC与FullGC分别在什么时候发生?

    新生代内存不够用时候发生MGC也叫YGC,JVM内存不够的时候发生FGC

    9.几种常用的内存调试工具:jmap、jstack、jconsole、jhat

    jstack可以看当前栈的情况,jmap查看内存,jhat进行dump堆的信息mat(eclipse的也要了解一下)

    10.类加载的几个过程:

    加载、验证、准备、解析、初始化。然后是使用和卸载了通过全限定名来加载生成class对象到内存中,然后进行验证这个class文件,包括文件格式校验、元数据验证,字节码校验等。准备是对这个对象分配内存。解析是将符号引用转化为直接引用(指针引用),初始化就是开始执行构造器的代码。

    11.JVM内存分哪几个区,每个区的作用是什么?

    java虚拟机主要分为以下一个区:

    方法区:

    1.有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载2.方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。

    3.该区域是被线程共享的。

    4.方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。

    虚拟机栈:

    1.虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。

    2.虚拟机栈是线程私有的,它的生命周期与线程相同。

    3.局部变量表里存储的是基本数据类型、returnAddress类型(指向一条字节码指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定

    4.操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式

    5.每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用

    本地方法栈

    本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。

    java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。

    程序计数器

    内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区域是唯一一个java虚拟机规范没有规定任何OOM情况的区域。

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

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

    1.引用计数法

    所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收.引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。

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

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

    虚拟机栈中引用的对象

    方法区类静态属性引用的对象

    方法区常量池引用的对象

    本地方法栈JNI引用的对象

    虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比不一定会被回收。当一个对象不可达GCRoot时,这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记如果对象在可达性分析中没有与GCRoot的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已被虚拟机调用过,那么就认为是没必要的。

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

    展开全文
    suikui 2021-09-10 16:01:38
  • jvm面试题 一、内存模型 1.jvm内存结构 程序计数器 用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令。 每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期...

    jvm面试题

    一、基础知识

    1.JDK,JRE,JVM区别

    概述

    Jdk中包括了Jre,Jre中包括了JVM

    jdk

    Jdk还包括了一些Jre之外的东西 ,就是这些东西帮我们编译Java代码的, 还有就是监控Jvm的一些工具 Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。

    jre

    Jre大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库 Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。

    jvm

    Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

    二、内存模型

    1.jvm内存结构

    在这里插入图片描述

    程序计数器

    用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令。

    每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。

    java虚拟机栈

    每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应着一次次的方法调用。

    是线程私有的。

    在这里插入图片描述

    本地方法栈

    java虚拟机栈用于管理java方法的调用,而本地方法栈用于管理本地方法的调用。

    是线程私有的。

    java堆

    java堆区在jvm启动的时候即被创建,其空间大小也就确定了。是jvm管理的最大一块内存空间。

    所有的线程共享java堆。

    堆是GC执行垃圾回收的重点区域。

    方法区

    方法区看作是独立java堆的内存空间。

    是各个线程共享的内存区域,在jvm启动的时候被创建。

    在jdk1.7及以前,习惯上把方法区,称为永久代。
    jdk8开始,使用元空间取代了永久代。

    元空间与永久代最大的区别在于,元空间不在虚拟机设置的内存中,而是使用本地内存。

    它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编辑器编译后的代码缓存等。

    三、类加载

    1.类的加载过程

    加载

    1)通过一个类的全限定名获取定义此类的二进制字节流
    2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口

    链接

    验证

    目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。

    准备

    1)为类变量分配内存并且设置该类变量的默认初始值,即零值。

    2)这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化。

    3)这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到堆中。

    解析

    1)将常量池内的符号引用转换为直接引用的过程。

    2)符号引用就是一组符号来描述所引用的目标。

    3)直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。

    4)事实上,解析操作往往会伴随着jvm在执行完初始化之后再执行。

    初始化

    1)初始化阶段就是执行类构造器方法()的过程。

    2)此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。(如果代码中不存在类变量和静态代码块,则没有()方法)

    3)构造器方法中指令按语句在源文件中出现的顺序执行。

    4)() 不同于类的构造器。(关联:构造器是虚拟机视角下的())

    5)若该类具有父类,jvm会保证子类的()执行前,父类的()已经执行完毕。

    6)虚拟机必须保证一个类的()方法在多线程下被同步加锁。

    2.类加载器

    启动类加载器(Bootstrap ClassLoader)

    1)这个类加载器使用C/C++语言实现的,嵌套在jvm内部

    2)它用来加载java核心类库

    扩展类加载器(extensions classloader)\

    它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

    应用程序类加载器(system class loader)

    它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。

    用户自定义类加载器

    通过继承 java.lang.ClassLoader类的方式实现。

    3.双亲委派机制

    在这里插入图片描述

    四、创建一个对象的过程

    1.判断对象对应的类是否加载、链接、初始化

    2.为对象分配内存

    3.处理并发安全问题

    4.初始化分配到空间

    所有属性设置默认值,保证对象实例字段在不赋值时可以直接使用

    5.设置对象的对象头

    6.执行init方法进行初始化

    五、堆空间

    1.堆空间分区

    在这里插入图片描述

    分区比例

    在这里插入图片描述

    六、垃圾回收

    1.java中的几种引用方式

    强引用 - 不回收

    概念

    在这里插入图片描述

    特点

    1)强引用可以直接访问目标对象

    2)强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指向对象。

    3)强引用可能导致内存泄漏。

    软引用 - 内存不足即回收

    在这里插入图片描述

    弱引用 - 发现即回收

    在这里插入图片描述

    虚引用 - 对象回收跟踪

    在这里插入图片描述

    2.垃圾回收机制

    1)自动内存管理
    无需开发人员手动的参与内存的分配与回收,这样降低内存泄漏和内存溢出的风险。

    自动内存管理机制,将程序员从繁重的内存管理中释放出来,可以更专心的专注于业务开发。

    2)垃圾回收器可以对年轻代回收,也可以对老年代回收,甚至是全堆或方法区的回收。

    什么时候回收

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    3.垃圾回收算法

    标记阶段

    在这里插入图片描述

    引用计数算法

    在这里插入图片描述

    可达性分析算法
    概述

    在这里插入图片描述

    实现思路

    在这里插入图片描述
    在这里插入图片描述

    GC Roots

    在这里插入图片描述

    清除阶段

    标记-清除算法
    概念

    在这里插入图片描述
    在这里插入图片描述

    缺点

    在这里插入图片描述

    标记-压缩(整理)算法
    概念

    将存活的内存空间分为两块,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收。

    在这里插入图片描述

    优缺点

    在这里插入图片描述

    应用场景

    在这里插入图片描述

    标记-压缩(整理)算法
    出现背景

    在这里插入图片描述

    具体流程

    在这里插入图片描述

    指针碰撞

    在这里插入图片描述

    优缺点

    在这里插入图片描述

    分代收集算法

    在这里插入图片描述

    增量收集算法
    概述

    在这里插入图片描述

    缺点

    在这里插入图片描述

    分区算法

    在这里插入图片描述
    在这里插入图片描述

    4.垃圾回收器

    前置知识

    内存溢出

    没有空闲内存,并且垃圾收集器也无法提供更多内存。

    内存泄漏
    概述

    对象不会再被程序用到了,但是GC又不能回收它们的情况

    场景举例

    在这里插入图片描述

    Stop the World

    在这里插入图片描述

    垃圾回收器分类

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    评估GC的性能指标
    吞吐量

    在这里插入图片描述

    暂停时间

    在这里插入图片描述

    7种经典垃圾回收器

    在这里插入图片描述
    在这里插入图片描述

    传统垃圾收集器介绍

    Serial回收器:串行回收

    在这里插入图片描述

    ParNew回收器:并行回收

    在这里插入图片描述

    Parallel回收器:吞吐量优先

    在这里插入图片描述
    在这里插入图片描述

    CMS回收器:低延迟

    概述

    在这里插入图片描述
    在这里插入图片描述

    工作的四个阶段

    在这里插入图片描述

    Concurrent Mode Failure

    在这里插入图片描述

    优缺点

    在这里插入图片描述

    G1回收器:区域化分代式

    优点

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    缺点

    在这里插入图片描述

    适用场景

    在这里插入图片描述

    Remembered Set

    在这里插入图片描述

    回收过程

    在这里插入图片描述

    promotion failed

    该问题是在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的。

    展开全文
    xiaotai1234 2021-05-28 20:22:30
  • 1. 描述一下jvm内存模型,以及这些空间的存放的内容 ? 2.堆内存划分的空间,如何回收这些内存对象,有哪些回收算法? 垃圾回收算法: 标记清除、复制(多为新生代垃圾回收使用)、标记整理 3.如何解决线上gc频繁...

    1. 描述一下jvm内存模型,以及这些空间的存放的内容 ?

    1. 描述一下jvm内存模型,以及这些空间的存放的内容 ?

    2.堆内存划分的空间,如何回收这些内存对象,有哪些回收算法?

    在这里插入图片描述
    垃圾回收算法: 标记清除、复制(多为新生代垃圾回收使用)、标记整理

    3.如何解决线上gc频繁的问题?

    1. 查看监控,以了解出现问题的时间点以及当前FGC的频率(可对比正常情况看频率是否正常)

    2. 了解该时间点之前有没有程序上线、基础组件升级等情况。

    3. 了解JVM的参数设置,包括:堆空间各个区域的大小设置,新生代和老年代分别采用了哪些垃 圾收集器,然后分析JVM参数设置是否合理。

    4. 再对步骤1中列出的可能原因做排除法,其中元空间被打满、内存泄漏、代码显式调用gc方法 比较容易排查。

    5. 针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文件作进一步分析,需要先定位到可疑对象。

    6. 通过可疑对象定位到具体代码再次分析,这时候要结合GC原理和JVM参数设置,弄清楚可疑 对象是否满足了进入到老年代的条件才能下结论。

    4.描述一下class初始化过程?

    一个类初始化就是执行clinit()方法,过程如下:

    • 父类初始化
    • static变量初始化/static块(按照文本顺序执行)

    Java Language Specification中,类初始化详细过程如下(最重要的是类初始化是线程安全的):

    1. 每个类都有一个初始化锁LC,进程获取LC(如果没有获取到,就一直等待)

    2. 如果C正在被其他线程初始化,释放LC并等待C初始化完成

    3. 如果C正在被本线程初始化,即递归初始化,释放LC

    4. 如果C已经被初始化了,释放LC

    5. 如果C处于erroneous状态,释放LC并抛出异常NoClassDefFoundError

    6. 否则,将C标记为正在被本线程初始化,释放LC;然后, 初始化那些final且为基础类型的类成员变量

    7. 初始化C的父类SC和各个接口SI_n(按照implements子句中的顺序来) ;如果SC或SIn初始化过程中抛出异常,则获取LC,将C标记为erroneous,并通知所有线程,然后释放LC,然后 再抛出同样的异常。

    8. 从classloader处获取assertion是否被打开

    9. 接下来, 按照文本顺序执行类变量初始化和静态代码块,或接口的字段初始化,把它们当作是一个个单独的代码块。

    10. 如果执行正常,获取LC,标记C为已初始化,并通知所有线程,然后释放LC

    11. 否则,如果抛出了异常E。若E不是Error,则以E为参数创建新的异常ExceptionInInitializerError作为E。如果因为OutOfMemoryError导致无法创建ExceptionInInitializerError,则将OutOfMemoryError作为E。

    12. 获取LC,将C标记为erroneous,通知所有等待的线程,释放LC,并抛出异常E

    5.简述一下内存溢出的原因,如何排查线上问题?

    内存溢出的原因

    • java.lang.OutOfMemoryError: …java heap space. 堆栈溢出,代码问题的可能性极大

    • java.lang.OutOfMemoryError: GC over head limit exceeded 系统处于高频的GC状态,而且回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放 的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.

    • java.lang.OutOfMemoryError: PermGen space jdk1.7之前才会出现的问题 ,原因是系统的代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、 或者通过动态代码加载等方法,导致常量池的膨胀

    • java.lang.OutOfMemoryError: Direct buffer memory 直接内存不足,因为jvm垃圾回收不会回收掉直接内存这部分的内存,所以可能原因是直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而没有做clear

    • java.lang.StackOverflowError - Xss设置的太小了

    • java.lang.OutOfMemoryError: unable to create new native thread 堆外内存不足,无法为线程分配内存区域

    • java.lang.OutOfMemoryError: request {} byte for {}out of swap 地址空间不够

    6.jvm有哪些垃圾回收器,实际中如何选择?

    在这里插入图片描述
    图中展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,则说明它们可以搭配使用。虚 拟机所处的区域则表示它是属于新生代还是老年代收集器。

    新生代收集器(全部的都是复制算法):Serial、ParNew、Parallel Scavenge

    老年代收集器:CMS(标记-清理)、Serial Old(标记-整理)、Parallel Old(标记整理) 整堆收集器: G1(一个Region中是标记-清除算法,2个Region之间是复制算法)

    同时,先解释几个名词:

    1. 并行(Parallel):多个垃圾收集线程并行工作,此时用户线程处于等待状态
    2. 并发(Concurrent):用户线程和垃圾收集线程同时执行
    3. 吞吐量:运行用户代码时间/(运行用户代码时间+垃圾回收时间)

    1.Serial收集器是最基本的、发展历史最悠久的收集器。

    特点: 单线程、简单高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,Serial收集器 由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程手机效率。收集器进行垃圾回收 时,必须暂停其他所有的工作线程,直到它结束(Stop The World)。

    应用场景: 适用于Client模式下的虚拟机。

    Serial / Serial Old收集器运行示意图:
    在这里插入图片描述
    2.ParNew收集器其实就是Serial收集器的多线程版本。

    除了使用多线程外其余行为均和Serial收集器一模一样(参数控制、收集算法、Stop The World、对象分配规则、回收策略等)。

    特点: 多线程、ParNew收集器默认开启的收集线程数与CPU的数量相同,在CPU非常多的环境中,可以 使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

    和Serial收集器一样存在Stop The World问题

    应用场景: ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为它是除了
    Serial收集器外,唯一一个能与CMS收集器配合工作的。

    ParNew/Serial Old组合收集器运行示意图如下:

    在这里插入图片描述
    3.Parallel Scavenge 收集器与吞吐量关系密切,故也称为吞吐量优先收集器。

    特点: 属于新生代收集器也是采用复制算法的收集器,又是并行的多线程收集器(与ParNew收集器类 似)。
    该收集器的目标是达到一个可控制的吞吐量。还有一个值得关注的点是:GC自适应调节策略(与
    ParNew收集器最重要的一个区别)

    GC自适应调节策略: Parallel Scavenge收集器可设置-XX:+UseAdptiveSizePolicy参数。当开关打开时不需要手动指定新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代 的对象年龄(-XX:PretenureSizeThreshold)等,虚拟机会根据系统的运行状况收集性能监控信息,动 态设置这些参数以提供最优的停顿时间和最高的吞吐量,这种调节方式称为GC的自适应调节策略。

    Parallel Scavenge收集器使用两个参数控制吞吐量:
    XX:MaxGCPauseMillis 控制最大的垃圾收集停顿时间
    XX:GCRatio 直接设置吞吐量的大小。

    4.Serial Old是Serial收集器的老年代版本。

    特点: 同样是单线程收集器,采用标记-整理算法。
    应用场景: 主要也是使用在Client模式下的虚拟机中。也可在Server模式下使用。
    Server模式下主要的两大用途(在后续中详细讲解···):

    1. 在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用。
    2. 作为CMS收集器的后备方案,在并发收集Concurent Mode Failure时使用。

    Serial / Serial Old收集器工作过程图(Serial收集器图示相同):

    在这里插入图片描述
    5.Parallel Old是Parallel Scavenge收集器的老年代版本。

    特点: 多线程,采用标记-整理算法。
    应用场景: 注重高吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old 收集器。

    6.CMS收集器是一种以获取最短回收停顿时间为目标的收集器。

    特点: 基于标记-清除算法实现。并发收集、低停顿。

    应用场景: 适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如web程序、b/s服务。

    CMS收集器的运行过程分为下列4步:

    初始标记: 标记GC Roots能直接到的对象。速度很快但是仍存在Stop The World问题。
    并发标记: 进行GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行。
    重新标记: 为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记 录。仍然存在Stop The World问题。
    并发清除: 对标记的对象进行清除回收。
    CMS收集器的内存回收过程是与用户线程一起并发执行的。

    CMS收集器的工作过程图:
    在这里插入图片描述
    CMS收集器的缺点:

    • 对CPU资源非常敏感。
    • 无法处理浮动垃圾,可能出现Concurrent Model Failure失败而导致另一次Full GC的产生。
    • 因为采用标记-清除算法所以会存在空间碎片的问题,导致大对象无法分配空间,不得不提前触发
      一次Full GC。

    在这里插入图片描述
    7.G1收集器一款面向服务端应用的垃圾收集器。

    特点如下:

    并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。

    分代收集:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。

    空间整合:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。

    可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。

    G1收集器运行示意图:

    在这里插入图片描述
    关于gc的选择
    除非应用程序有非常严格的暂停时间要求,否则请先运行应用程序并允许VM选择收集器(如果没有特别要求。使用VM提供给的默认GC就好)。

    如有必要,调整堆大小以提高性能。 如果性能仍然不能满足目标,请使用以下准则作为选择收集器的起点:

    • 如果应用程序的数据集较小(最大约100 MB),则选择带有选项-XX:+ UseSerialGC的串行收集器。
    • 如果应用程序将在单个处理器上运行,并且没有暂停时间要求,则选择带有选项-XX:+UseSerialGC的串行收集器
    • 如果(a)峰值应用程序性能是第一要务,并且(b)没有暂停时间要求或可接受一秒或更长时间的暂停,则让VM选择收集器或使用-XX:+ UseParallelGC选择并行收集器 。
    • 如果响应时间比整体吞吐量更重要,并且垃圾收集暂停时间必须保持在大约一秒钟以内,则选择具有-XX:+ UseG1GC。(值得注意的是JDK9中CMS已经被Deprecated,不可使用!移除该选项)
    • 如果使用的是jdk8,并且堆内存达到了16G,那么推荐使用G1收集器,来控制每次垃圾收集的时间。
    • 如果响应时间是高优先级,或使用的堆非常大,请使用-XX:UseZGC选择完全并发的收集器。(值得注意的是JDK11开始可以启动ZGC,但是此时ZGC具有实验性质,在JDK15中
    • [202009发布]才取消实验性质的标签,可以直接显示启用,但是JDK15默认GC仍然是G1)

    这些准则仅提供选择收集器的起点,因为性能取决于堆的大小,应用程序维护的实时数据量以及可用处理器的数量和速度。
    如果推荐的收集器没有达到所需的性能,则首先尝试调整堆和新生代大小以达到所需的目标。 如果性能仍然不足,尝试使用其他收集器
    总体原则:减少STOP THE WORD时间,使用并发收集器(比如CMS+ParNew,G1)来减少暂停时间,加快响应时间,并使用并行收集器来增加多处理器硬件上的总体吞吐量。

    7. 简述一下Java类加载模型?

    在这里插入图片描述
    双亲委派模型
    在某个类加载器加载class文件时,它首先委托父加载器去加载这个类,依次传递到顶层类加载器(Bootstrap)。如果顶层加载不了(它的搜索范围中找不到此类),子加载器才会尝试加载这个类。双亲委派的好处

    • 每一个类都只会被加载一次,避免了重复加载
    • 每一个类都会被尽可能的加载(从引导类加载器往下,每个加载器都可能会根据优先次序尝试加载它)
    • 有效避免了某些恶意类的加载(比如自定义了Java.lang.Object类,一般而言在双亲委派模型下会加载系统的Object类而不是自定义的Object类)

    9. JVM8为什么要增加元空间,带来什么好处?

    原因:

    1. 字符串存在永久代中,容易出现性能问题和内存溢出。
    2. 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢
      出,太大则容易导致老年代溢出。
    3. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

    元空间的特点:

    1. 每个加载器有专门的存储空间。
    2. 不会单独回收某个类。
    3. 元空间里的对象的位置是固定的。
    4. 如果发现某个加载器不再存货了,会把相关的空间整个回收。

    10. 堆G1垃圾收集器有了解么,有什么特点

    G1的特点:

    1. G1的设计原则是"首先收集尽可能多的垃圾(Garbage First)"。因此,G1并不会等内存耗尽(串行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找出具有高收集收益的分区进行收集。同时G1可以根据用户设置的暂停时间目标自动调整年轻代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大;
    2. G1采用内存分区(Region)的思路,将内存划分为一个个相等大小的内存分区,回收时则以分区为单位进行回收,存活的对象复制到另一个空闲分区中。由于都是以相等大小的分区为单位进行操作,因此G1天然就是一种压缩方案(局部压缩);
    3. G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换;
    4. G1的收集都是STW的,但年轻代和老年代的收集界限比较模糊,采用了混合(mixed)收集的方式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。
    5. 因为G1建立可预测的停顿时间模型,所以每一次的垃圾回收时间都可控,那么对于大堆(16G左右)的垃圾收集会有明显优势

    11. 介绍一下垃圾回收算法?

    标记-清除

    在这里插入图片描述
    缺点: 产生内存碎片,如上图,如果清理了两个1kb的对象,再添加一个2kb的对象,无法放入这两个位置

    标记-整理(老年代)

    在这里插入图片描述
    缺点:移动对象开销较大

    复制(新生代)

    在这里插入图片描述

    12. Happens-Before规则?

    先行发生原则(Happens-Before)是判断数据是否存在竞争、线程是否安全的主要依据。
    先行发生是Java内存,模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,那么操作A产生的影响能够被操作B观察到。

    口诀:如果两个操作之间具有happen-before关系,那么前一个操作的结果就会对后面的一个操作可见。是Java内存模型中定义的两个操作之间的偏序关系。

    常见的happen-before规则:
    1.程序顺序规则:
    一个线程中的每个操作,happen-before在该线程中的任意后续操作。(注解:如果只有一个线程的操作,那么前一个操作的结果肯定会对后续的操作可见。)
    程序顺序规则中所说的每个操作happen-before于该线程中的任意后续操作并不是说前一个操作必须要在后一个操作之前执行,而是指前一个操作的执行结果必须对后一个操作可见,如果不满足这个要求那就不允许这两个操作进行重排序
    2.锁规则:
    对一个锁的解锁,happen-before在随后对这个锁加锁。(注解:这个最常见的就是synchronized方法和syncronized块)
    3.volatile变量规则:
    对一个volatile域的写,happen-before在任意后续对这个volatile域的读。该规则在CurrentHashMap的读操作中不需要加锁有很好的体现。
    4.传递性:
    如果A happen-before B,且B happen-before C,那么A happen - before C.
    5.线程启动规则
    Thread对象的start()方法happen-before此线程的每一个动作。
    6.线程终止规则:
    线程的所有操作都happen-before对此线程的终止检测,可以通过Thread.join()方法结束,Thread.isAlive()的返回值等手段检测到线程已经终止执行。
    7.线程中断规则:
    对线程interrupt()方法的调用happen-before发生于被中断线程的代码检测到中断时事件的发生。

    13. 描述一下java类加载和初始化的过程?

    JAVA类的加载机制
    Java类加载分为5个过程,分别为:加载,链接(验证,准备,解析),初始化,使用,卸载。

    加载
    加载主要是将.class文件通过二进制字节流读入到JVM中。 在加载阶段,JVM需要完成3件事:
    1)通过classloader在classpath中获取XXX.class文件,将其以二进制流的形式读入内存。
    2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构;
    3)在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

    1. 链接
      2.1. 验证
      主要确保加载进来的字节流符合JVM规范。验证阶段会完成以下4个阶段的检验动作:
      1)文件格式验证
      2)元数据验证(是否符合Java语言规范) 3)字节码验证(确定程序语义合法,符合逻辑)
      4)符号引用验证(确保下一步的解析能正常执行)
      2.2. 准备
      准备是连接阶段的第二步,主要为静态变量在方法区分配内存,并设置默认初始值。
      2.3. 解析
      解析是连接阶段的第三步,是虚拟机将常量池内的符号引用替换为直接引用的过程。
    2. 初始化
      初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。
      当有继承关系时,先初始化父类再初始化子类,所以创建一个子类时其实内存中存在两个对象实
      例。
    3. 使用
      程序之间的相互调用。
    4. 卸载
      即销毁一个对象,一般情况下中有JVM垃圾回收器完成。代码层面的销毁只是将引用置为null。

    15. 吞吐量优先和响应时间优先的回收器是哪些?

    • 吞吐量优先:Parallel Scavenge+Parallel Old(多线程并行)
    • 响应时间优先:cms+par new(并发回收垃圾)

    16. 什么叫做阻塞队列的有界和无界,实际中有用过吗?

    • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列,线程池,生产者消费者
    • LinkedBlockingQueue:一个由链表结构组成的无界阻塞队列,线程池,生产者消费者
    • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列,可以实现精确的定时任务
    • DelayQueue:一个使用优先级队列实现的无界阻塞队列,可以实现精确的定时任务
    • SynchronousQueue:一个不存储元素的阻塞队列,线程池
    • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
    • LinkedBlockingDeque:一个由链表结构组成的双向无界阻塞队列,可以用在“工作窃取”模式

    17. jvm监控系统是通过jmx做的么?

    一般都是,但是要是记录比较详细的性能定位指标,都会导致进入 safepoint,从而降低了线上应用性能
    例如 jstack,jmap打印堆栈,打印内存使用情况,都会让 jvm 进入safepoint,才能获取线程稳定状态从而采集信息。
    同时,JMX暴露向外的接口采集信息,例如使用jvisualvm,还会涉及rpc和网络消耗,以及JVM忙时,无法采集到信息从而有指标断点。这些都是基于 JMX 的外部监控很难解决的问题。所以,推荐使用JVM内部采集 JFR,这样即使在JVM很忙时,也能采集到有用的信息

    18. 内存屏障的汇编指令是啥?

    1.硬件内存屏障 X86
    sfence: store| 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。
    lfence: load | 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。
    mfence: modify/mix | 在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。
    2.原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。
    3.JVM级别如何规范(JSR133)

    LoadLoad屏障:
    对于这样的语句Load1; LoadLoad; Load2, 在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
    StoreStore屏障:
    对于这样的语句Store1; StoreStore; Store2, 在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
    LoadStore屏障:
    对于这样的语句Load1; LoadStore; Store2, 在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
    StoreLoad屏障:
    对于这样的语句Store1; StoreLoad; Load2, 在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

    我是牧小农,怕什么真理无穷,进一步有进一步的欢喜,大家加油!

    展开全文
    qq_14996421 2021-04-25 20:29:38
  • m0_48795607 2021-04-09 17:11:16
  • u012889902 2021-12-15 14:05:41
  • qq_41522089 2021-03-31 16:50:08
  • weixin_57907028 2021-08-07 16:15:29
  • weixin_34324006 2021-03-09 23:53:18
  • weixin_42398843 2021-03-08 09:35:17
  • Vincent9847 2021-06-10 21:27:57
  • GV7lZB0y87u7C 2021-10-08 00:07:28
  • v123411739 2021-11-02 13:03:15
  • weixin_38378034 2021-01-12 11:55:26
  • u012889902 2021-12-01 09:53:25
  • crazymakercircle 2021-02-03 10:32:40
  • li_w_ch 2021-04-14 15:39:31
  • m0_51713294 2021-01-20 11:54:21
  • weixin_39841002 2021-03-08 09:35:05
  • itfly8 2020-12-31 20:44:12
  • codingToMaster 2021-03-07 16:36:38
  • weixin_47345084 2021-01-15 16:49:33
  • zuishengmengsi1990 2021-09-03 23:36:17
  • sgw827 2021-03-08 15:21:38
  • zpcandzhj 2021-02-20 18:02:57
  • qq_25210899 2021-02-03 17:35:51
  • const_ 2020-12-26 14:18:09
  • qq_43538493 2021-08-25 15:35:47
  • u012889902 2021-12-01 10:46:02

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 154,374
精华内容 61,749
关键字:

jvm面试