JVM面试题附带答案

1.JVM运行时结构,各个区存放的数据

按线程可见性来分可以分为下面这两类

  • 线程共享

    • 堆区:堆用来存放数组和对象,内部又分为了新生代和老年代,他们的比例默认为1:2,新生代又分为了一个Eden和两个Survivor,默认比例为8:1:1
    • 方法区:用来存放类的方法、属性、字符串常量池、静态变量等。在1.7的时侯,字符串常量和静态变量都放在永久代,在1.8的时候移除了永久代,永久代在启动之前必须制定初始大小,FGC还不会对它进行垃圾回收,新加入了元空间(MetaSpace),将原本存放在永久代的数据全部移到了元空间,元空间启动不需要制定大小,默认最大大小就是物理内存,FGC会对它进行垃圾回收。
  • 线程独占

    • 虚拟机栈:存放一个个栈帧,每个方法调用都会生成一个栈帧,用来存放局部变量表、操作数栈、动态链接、以及方法的出口信息
    • 本地方法区:存放线程可能会使用到的本地方法
    • PC寄存器:记录当前线程执行的指令

2.类加载机制

类加载一共分为以下三个步骤

1. 加载:把字节码文件load到内存中的方法区,生成一个Class对象,这个Class指向了这块内存

2. 连接:连接又分为了三个步骤

  • 验证:验证字节码文件是否符合JVM规范
  • 准备:给静态变量赋默认值
  • 解析:将类属性、方法的符号引用解析为直接引用,常量池的符号引用解析为指针

3. 初始化:执行静态代码块,给静态变量赋值

3.什么是双亲委派机制,为什么要使用双亲委派

JVM建议类加载时遵循的一种规范,每个类加载器都有自己的父类加载器,当一个加载器接收到一个加载任务,会先去自己的缓存中找,没找到就委派给父类的加载器,直到委派给顶层的加载器,缓存中还是没找到后,就开始尝试进行加载,加载失败就委托给自己的下一层类加载器去加载,如果到了底层的加载器还是没加载成功,就报ClassNotFoudException的异常。

使用双亲委派的原因:主要是为了安全,确保类的唯一性,防止用户自定义类覆盖掉了JDK核心的类。

4.什么是cpu的乱序问题,操作系统层面如何保证不乱序执行

cpu为了提高指令执行的效率,会在一条指令执行过程中,同时区执行另一条指令,前提是两条指令没有依赖关系

确保指令的顺序执行:

  • cpu的内存屏障
  • lock的汇编指令

5.对象的创建过程具体是怎样的

加载–>连接(验证、准备、解析)–>初始化–>申请内存空间–>成员变量赋默认值–>调用构造方法(成员变量赋初始值)

6.对象在内存中的存储布局

  • 对象

    • 对象头:8个字节
    • ClassPoint(指针):开了压缩(-XX:+UseCompressedClassPointers默认开启)是4个字节 不开8个字节
    • 实例数据:引用类型开了压缩是4个字节 不开是8个字节
    • Padding对齐:为了对齐8的倍数
  • 数组:数组就比普通对象多了个长度,4个字节

7.对象头中具体包含了那些信息

对象头占了8个字节,具体包含的信息如下(64位):

  • 锁信息:用了3位来表示,用了1位来表示是否偏向锁,另外两位用来标记锁状态
  • GC的分代年龄:用了4位来表示,也就是说对象的分代年龄最大就是15
  • 对象的HashCode值:占了31位

8.什么是垃圾

没有任何引用指向的对象就可以称为垃圾对象

9.寻找垃圾的算法

  • 引用计数器法:这个算法会有循环引用的问题
  • 根可达分析算法

10.GC Roots对象包括哪些

线程栈对象、静态变量、常量池、JNI指针(本地方法对象)

11.垃圾的清除算法,说说它们的优缺点

  • 标记清除

    • 优点:算法简单,存活对象较多的情况下,效率比较高
    • 缺点:需要扫描两遍,效率比较低,容易产生内存碎片
  • 拷贝算法

    • 优点:不会产生内存碎片,
    • 缺点:内存一分为二,浪费内存空间,移动复制对象需要调整对象引用
  • 标记压缩

    • 优点:不会内存减半,也不会产生内存碎片,方便对象分配
    • 缺点:两次扫描,移动对象需要调整对象引用,效率较低

12.什么是栈上分配、线程本地分配TLAB

  • 栈上分配:用来分配那些不会逃逸出方法的对象,使用普通类型来代替对象

  • TLAB(Thread Local Allocation Buffer):线程创建时,申请的一块内存,默认占整个Eden的1%(可以通过参数配置),小且无逃逸对象分配时会先尝试线程本地分配

    • 优点:多线程情况不用竞争Eden,就可以申请内存空间,提高了效率

13.什么是动态年龄

触发YGC时,Eden和其中一个Survivor存活的对象,内存占比大于另一个Survivor区的50%时,一些年龄比较大的对象会被移到老年代

14.什么是分配担保

触发YGC时,如果有对象进来,内存不足,就会将对象直接分配到老年代,这个就是担保机制

15.常见的垃圾回收器有哪一些

  • Serial:分代模型,单线程最快的垃圾回收器,搭配Serial Old使用

  • Serial Old

  • Parallel Scavenge:分代模型,并发清理,搭配Parallel Old使用

  • Parallel Old

  • ParNew:分代模型,为了配合CMS使用而衍生出来的,其实就是升级版的Parallel Scavenge

  • CMS:分代模型,开启了并行回收的先河,但是毛病比较多,会产生内存碎片、浮动垃圾

  • G1:逻辑分代,物理不分,JDK1.7加入的,将内存分为了一个个分区,每个分区都可以表示Eden、Survivor、Old、Humongous

  • ZGC:采用颜色指针来找垃圾对象

  • Shenandoah:采用颜色指针来找垃圾对象

  • Epsilon:调试用的不是很重要

16.CMS的回收步骤,它有什么问题,怎么避免这些问题

  • 初始标记:找到GCRoots对象,时间比较短(STW)
  • 并发标记:耗时最长的一步,采用并发执行
  • 重新标记:找到漏标的对象,重新找那些垃圾对象(STW)
  • 并发清除:会产生浮动垃圾,这些垃圾会在下一次GC的时候清理掉

问题:会有内存碎片和浮动垃圾,降低触发CMS的阈值,

17.Hospot参数分类有哪些

  • -:标准参数,所有的Hotspot都支持
  • -X:非标参数,特定版本的Hotspot支持
  • -XX:不稳定参数,下个版本可能会删除

18.什么是性能调优

  • 根据业务需求对JVM进行规划和预调优
  • 优化JVM的运行环境(慢、卡顿)
  • 解决JVM运行过程中出现的各种问题(OOM)

19.说一下调优中的吞吐量和响应时间

吞吐量 = 用户代码执行时间/(用户代码执行时间 + GC回收时间)

响应时间:STW越短,响应时间越好

20.说一下调优的具体步骤

1. 首先通过top或Jps命令找到程序的pid

2. 通过jstack查看进程中,线程的具体运行情况,注意那些线程状态为WAITTING和BLOCKED的线程

3. 通过jmap -histo命令找出程序中哪些对象占用内存比较大

4. 通过上面这些信息找到问题,然后做出相应的解决

21.说一下三色标记算法

垃圾标记的一种算法,将对象分为三种颜色

  • 黑色:自身和属性都被标记的对象
  • 灰色:自身被标记对象,但是属性还没有标记
  • 白色:未被标记的对象

22.说一下G1的内部结构和特点

G1是JDK1.7的时候加入的,它严格意义来讲不属于分代模型,G1逻辑分代,物理不分,它把内存分为了一个个分区,每个分区都可以代表Eden、Survivor、Old、Humongous。

特点:

  • 压缩空闲空间不会延长暂停时间

  • 更易预测GC的停顿时间

  • 适用并发不是特别高的引用场景

23.G1会触发那些GC

YGC、Mixed GC、FGC

24.什么是CSet(Collect Set)、Card Table、以及什么是Rset(Remember Table)

  • CSet:一组可被回收分区的集合,GC过程中,存活的对象会被移到其它的分区,CSet里面的分区可能来自 Eden、Surviror、Old
  • Card Table:由于YGC需要扫描整个Old区,JVM设计了Card Table,如果有对象指向Y区,那么将这个Card Table设置为Dirty,下次只需要扫描这些Dirt Card就行
  • RSet:记录了其它分区到本分区的对象引用,它的价值在于,垃圾回收器不需要扫描整个堆,找到对象引用,只需要遍历RSet即可

25.G1发生FGC是使用哪个垃圾回收器来回收的,怎么优化G1的FGC

G1发生FGC的话,是采用单线程的Serial来回收的,所以尽量不要让G1发生FGC,我们可以扩大内存、增加CPU的响应速度、降低Mixed GC的触发阈值(默认45%)来对它进行优化

26.什么是Mixed GC

它类似于CMS,回收步骤也有四个分为为:

  • 初始标记:找到GCRoots对象(STW)
  • 并发标记:耗时最长的一个步骤
  • 最终标记:找到漏标对象,扫描并找到垃圾对象(时间特别短,也是STW)
  • 筛选回收:STW(并行)

27.什么是漏标,什么情况下会发生漏标,解决方案有哪些

一个存活的对象,由于没有被GC扫描到,导致被回收掉

发生漏标的两种情况

  • 在remark阶段,黑色指向白色,没有对黑色对象重新扫描时,白色对象会被当成垃圾回收掉。
  • 在并发标记阶段,灰色指向白色的引用失效,此时白色对象应该被回收掉。

解决方案

  • 增量更新:关注引用增加,如果有黑色指向白色,将黑色改为灰色,下一次扫描它的属性(CMS使用的就是这种方式)
  • STAB:关注引用删除,如果灰色指向白色引用失效,将白色引用推到GC的堆栈,确保能够被扫描到(G1使用的就是这种方式)

28.为什么G1处理漏标要使用STAB而不使用增量更新

由于G1中RSet的存在,不需要遍历整个堆区去找白色对象的引用,效率比较高

29.Arthas如何来排查系统问题

  • 通过dashboard命令观察线程和堆得使用情况
  • 通过Thread pid命令查看线程内部的运行情况
  • 通过jvm命令查看虚拟机的详细信息

你可能感兴趣的:(jvm)