10-JVM面试相关

nobility 发布于 2021-07-24 1717 次阅读


JVM面试相关

字节码方面

Integer x = 5;int y = 5比较x == y都经历了哪些步骤?

  1. 首先将常量5放入操作数栈栈顶
  2. 再调用Integer.valueOf()方法将常量5封装成对象放入操作数栈栈顶
  3. 再将操作数栈栈顶的内容存入slot的一个位置,也就是变量赋值
  4. 又一次将常量5放入操作数栈栈顶
  5. 又将操作数栈栈顶的内容存入slot的一个位置,也就是变量赋值
  6. 将slot的一个位置中的内容入操作数栈,也就是取出x的值
  7. 再调用Integer.intValue()方法将对象5转化成数字5
  8. 将slot的一个位置中的内容入操作数栈,也就是取出y的值
  9. 将操作数栈中栈顶和次栈顶数进行比较
  10. 比较成功跳转到为某个变量赋值为1的指令,否则跳转到为某个变量赋值为0的指令

类加载、连接和初始化方面

简述Java的类加载机制

思路:

  1. 首先简述类加载功能和类加载器有哪些
  2. 从而引入类加载器关系
  3. 之后最主要的就是将双亲委派模型简述出来

JVM中释放可存在两个相同的类?

每个类加载器都有自己的命名空间,命名空间由该类加载器及其所有父加载器所加载的类构成,不同的命名空间,可出现类全限定名相同的情况(是对于同时启动两遍程序的情况,这两套程序运行时类加载器的类限定名是相同的),运行时包由同一个类加载器的类构成,决定两个类是否属于同一个运行时包,不仅要看全限定类名是否一致,还要看定义类加载器是否相同,只有属于同一个运行时包的类才能实现相互包内可见

类加载、连接和初始化的过程

思路:简述类的生命周期,并简述某个生命周期的作用

内存分配方面

简谈Java内存模型

思路:根据语境,看是内存分配模型,还是内存交互模型,大多数情况下是内存分配模型

  • 内存分配模型:简述虚拟机栈、本地方法栈、堆、方法区、程序计数器、代码缓存区的作用
  • 内存交互模型:简述变量从主内存拷贝到工作内存,从工作内存同步回主内存,交互的几个操作

Java堆内存一定是线程共享的吗?

为对象分配内存简单的说,就是从内存区域中划出一块,让一个对象的引用指向这块区域,再对对象初始化就可以使用了,其中从内存区域划分一块区域的细节有:指针碰撞法和空闲列表法

  • 指针碰撞法:假设Java堆内存没有内存碎片(这显然是不现实的),通过一个指针作为分界点,一侧已经使用,一侧未被使用,当要分配内存时,将指针向未被使用这一侧移动想分配的大小即可
  • 空闲列表法:JVM通过维护一个记录着可用的内存块的信息的列表,当要分配内存时,从该列表中寻找一个足够大的内存块进行分配,然后更新列表

因为堆是全局共享的,所以在同一个时间有可能多个线程在堆上申请空间,就有可能会出现多个线程申请同一块空间,所以JVM必须要保证内存分配是线程安全的,通常对于内存分配并发问题解决,同时使用:CAS和TLAB

  • CAS:乐观锁,假设没有冲突,若有冲突,失败后再重新申请,直到成功分配未知
  • TLAB:线程本地分配缓冲区,JVM为每个线程预先分配一块内存,JVM在为线程中的对象分配内存时,首先在这块区域进行分配,当这块区域不够时,在采用CAS方式进行分配

也就是说虽然TLAB这块区域虽然是在堆上,但是是线程独有的,而并非是线程共享的,并且独享仅是在分配这个动作上是独享的,一旦分配完成过后在后续使用上属于线程共享的;其实就是JVM为了加快内存分配的效率,并且避免出现并发冲突而提前为每个线程预分配的一块区域,通常来说只有Eden区域的1%左右

综上所述,较真的来说Java堆内存也一定是线程共享

简述JVM的堆内存

思路:按照分代的思路简述堆内存区域和每个区域的作用

什么情况下会触发GC?会触发哪些GC?

思路:首先讲述GC的类型,当新生代内存不足时可能会触发新生代的GC,当老年代内存不足时可能会触发老年代的GC或FullGC

垃圾回收方面

简谈JVM垃圾回收

思路:

  1. 简述如何判定垃圾:引用计数法和可达性分析法,以及判断是否是垃圾的步骤
  2. 简述三个垃圾回收算法:标记清除法、复制算法和标记整理法
  3. 简述垃圾收集器

简述JVM中四种引用类型

思路:简述引用类型即可,弱引用WeakReferenceThreadLocal中的静态内部类ThreadLocal.ThreadLocalMap的key就是使用的弱引用

监控工具和实战应用方面

dump内存的几种方式

  • JVM启动加参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath参数,在出现内存溢出错误时会生成dump日志,使用VisualVM工具可进行分析
  • 使用jmap -dump:format=b,file=文件名 pid命令指定进程号,会生成dump日志,使用VisualVM工具可进行分析
  • 图形化工具jmc和VisualVM中点击dump按钮

定位问题和解决问题的思路和方法

思路:通常情况下同时使用jmc和VisualVM,通过jmc记录一段时间的飞行记录,通过VisualVM实时查看JVM的状况,再简述JVM调优步骤,以及内存泄漏问题的发现和解决

CPU使用率过高怎么办

思路:使用jmc和VisualVM工具进行跟踪和分析,查看是哪一个热门的线程、方法、类

  • 本身就是计算密集型应用,进行算法的优化
  • 有可能是大量的IO消耗的CPU
  • 大量的线程死锁,或线程竞争资源导致消耗CPU过高
  • 等等

线上应用频繁FullGC如何处理

思路:使用jmc和VisualVM工具进行跟踪和分析

  • 可能整个堆内存设置过小
  • 可能新生代和老年代的比例不够好
  • 可能代码在频繁的分配大对象
  • 可能使用垃圾收集器不合理,或垃圾收集器参数设置不合理

应用周期性出现卡顿,如何排查

思路:使用jmc和VisualVM工具进行跟踪和分析,多半是FullGC导致的STW,规避FullGC的发生

如何处理OutOfMemoryError错误

思路:简述内存泄漏问题的发现和解决

如何处理StackOverflowError错误

每个线程默认开启会开启1MB的堆栈空间,用于存放栈帧、调用参数、局部变量等,对大多数应用而言,该默认值太大了,一般使用128KB、256KB足以(使用-Xss参数调整堆栈空间),若出现栈溢出抛出java.lang.StackOverflowError异常,一般都是递归调用没有退出或循环调用造成的,在内存不变的情况下,减少线程堆栈,可产生更多的线程

此作者没有提供个人介绍
最后更新于 2021-07-24