40 道JVM面试题解答

2020-12-16

1、对象在哪块内存分配?

数组和对象在堆内存分配;某些对象没有逃逸出方法,可能被优化为在栈上分配

 

2、谈谈 JVM 中的常量池

JDK 1.8 开始

字符串常量池:存放在堆中,包括 String 对象执行 intern() 方法后存的地方、双引号直接引用的字符串

运行时常量池:存放在方法区,属于元空间,是类加载后的一些存储区域,大多数是类中 constant_pool 的内容

类文件常量池:constant_pool,JVM 定义的概念

 

3、谈谈动态年龄判断

这里涉及到 -XX:TargetSurvivorRatio 参数,Survivor 区的目标使用率默认 50,即 Survivor 区对象目标使用率为 50%。


Survivor 区相同年龄所有对象大小的总和 > (Survivor 区内存大小 * 这个目标使用率)时,大于或等于该年龄的对象直接进入老年代。


当然,这里还需要考虑参数 -XX:MaxTenuringThreshold 晋升年龄最大阈值

 

4、谈谈永久代

JDK 8 之前,Hotspot 中方法区的实现是永久代(Perm)

JDK 7 开始把原本放在永久代的字符串常量池、静态变量等移出到堆,JDK 8 开始去除永久代,使用元空间(Metaspace),永久代剩余内容移至元空间,元空间直接在本地内存分配。

 

5、JVM 有哪些运行时内存区域?

Java 8

  • The pc Register,程序计数器

  • Java Virtual Machine Stacks,Java 虚拟机栈

  • Heap,堆

  • Method Area,方法区

  • Run-Time Constant Pool,运行时常量池

  • Native Method Stacks,本地方法栈

摘自:

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

 

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

  • 局部变量表

  • 操作数栈

  • 动态连接

  • 返回地址

  • 附加信息

 

7、JVM 的内存模型是什么?

JVM 试图定义一种统一的内存模型,能将各种底层硬件以及操作系统的内存访问差异进行封装,使 Java 程序在不同硬件以及操作系统上都能达到相同的并发效果。它分为工作内存和主内存,线程无法对主存储器直接进行操作,如果一个线程要和另外一个线程通信,那么只能通过主存进行交换。

 

8、JVM 如何确定垃圾对象?

JVM 采用的是可达性分析算法,通过 GC Roots 来判定对象是否存活,从 GC Roots 向下追溯、搜索,会产生 Reference Chain。当一个对象不能和任何一个 GC Root 产生关系时,就判定为垃圾。

 

软引用和弱引用,也会影响对象的回收。内存不足时会回收软引用对象;GC 时会回收弱引用对象。

 

9、哪些是 GC Roots?

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。

  • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。

  • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。

  • 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。

  • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如 NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

  • 所有被同步锁(synchronized关键字)持有的对象。

  • 反映 Java 虚拟机内部情况的 JMXBean、JVMTI中注册的回调、本地代码缓存等。

 

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

不一定,看 Reference 类型,弱引用在 GC 时会被回收,软引用在内存不足的时候,即 OOM 前会被回收,但如果没有在 Reference Chain 中的对象就一定会被回收。

 

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

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

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

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

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

 

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

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

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

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

 

13、工作中常用的 JVM 配置参数有哪些?

Java 8 为例

日志
-XX:+PrintFlagsFinal,打印JVM所有参数的值
-XX:+PrintGC,打印GC信息
-XX:+PrintGCDetails,打印GC详细信息
-XX:+PrintGCTimeStamps,打印GC的时间戳
-Xloggc:filename,设置GC log文件的位置
-XX:+PrintTenuringDistribution,查看熬过收集后剩余对象的年龄分布信息

内存设置
-Xms,设置堆的初始化内存大小
-Xmx,设置堆的最大内存
-Xmn,设置新生代内存大小
-Xss,设置线程栈大小
-XX:NewRatio,新生代与老年代比值
-XX:SurvivorRatio,新生代中Eden区与两个Survivor区的比值,默认为8,即Eden:Survivor:Survivor=8:1:1
-XX:MaxTenuringThreshold,从年轻代到老年代,最大晋升年龄。CMS 下默认为 6,G1 下默认为 15
-XX:MetaspaceSize,设置元空间的大小,第一次超过将触发 GC
-XX:MaxMetaspaceSize,元空间最大值
-XX:MaxDirectMemorySize,用于设置直接内存的最大值,限制通过 DirectByteBuffer 申请的内存
-XX:ReservedCodeCacheSize,用于设置 JIT 编译后的代码存放区大小,如果观察到这个值有限制,可以适当调大,一般够用即可

设置垃圾收集相关
-XX:+UseSerialGC,设置串行收集器
-XX:+UseParallelGC,设置并行收集器
-XX:+UseConcMarkSweepGC,使用CMS收集器
-XX:ParallelGCThreads,设置Parallel GC的线程数
-XX:MaxGCPauseMillis,GC最大暂停时间 ms
-XX:+UseG1GC,使用G1垃圾收集器

CMS 垃圾回收器相关
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction,与前者配合使用,指定MajorGC的发生时机
-XX:+ExplicitGCInvokesConcurrent,代码调用 System.gc() 开始并行 FullGC,建议加上这个参数
-XX:+CMSScavengeBeforeRemark,表示开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试,它可以降低 remark 时间,建议加上
-XX:+ParallelRefProcEnabled,可以用来并行处理 Reference,以加快处理速度,缩短耗时

G1 垃圾回收器相关
-XX:MaxGCPauseMillis,用于设置目标停顿时间,G1 会尽力达成
-XX:G1HeapRegionSize,用于设置小堆区大小,建议保持默认
-XX:InitiatingHeapOccupancyPercent,表示当整个堆内存使用达到一定比例(默认是 45%),并发标记阶段就会被启动
-XX:ConcGCThreads,表示并发垃圾收集器使用的线程数量,默认值随 JVM 运行的平台不同而变动,不建议修改

参数查询官网地址:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

 

建议面试时最好能记住 CMS 和 G1的参数,特点突出使用较多,被问的概率大

 

14、谈谈对 OOM 的认识

除了程序计数器,其他内存区域都有 OOM 的风险。

  • 栈一般经常会发生 StackOverflowError,比如 32 位的 windows 系统单进程限制 2G 内存,无限创建线程就会发生栈的 OOM

  • Java 8 常量池移到堆中,溢出会出 java.lang.OutOfMemoryError: Java heap space,设置最大元空间大小参数无效

  • 堆内存溢出,报错同上,这种比较好理解,GC 之后无法在堆中申请内存创建对象就会报错

  • 方法区 OOM,经常会遇到的是动态生成大量的类、jsp 等

  • 直接内存 OOM,涉及到 -XX:MaxDirectMemorySize 参数和 Unsafe 对象对内存的申请

 

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

-Xss可以设置线程栈的大小,当线程方法递归调用层