JVM面试相关
字节码方面
Integer x = 5;int y = 5
比较x == y
都经历了哪些步骤?
- 首先将常量5放入操作数栈栈顶
- 再调用Integer.valueOf()方法将常量5封装成对象放入操作数栈栈顶
- 再将操作数栈栈顶的内容存入slot的一个位置,也就是变量赋值
- 又一次将常量5放入操作数栈栈顶
- 又将操作数栈栈顶的内容存入slot的一个位置,也就是变量赋值
- 将slot的一个位置中的内容入操作数栈,也就是取出x的值
- 再调用Integer.intValue()方法将对象5转化成数字5
- 将slot的一个位置中的内容入操作数栈,也就是取出y的值
- 将操作数栈中栈顶和次栈顶数进行比较
- 比较成功跳转到为某个变量赋值为1的指令,否则跳转到为某个变量赋值为0的指令
类加载、连接和初始化方面
简述Java的类加载机制
思路:
- 首先简述类加载功能和类加载器有哪些
- 从而引入类加载器关系
- 之后最主要的就是将双亲委派模型简述出来
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垃圾回收
思路:
- 简述如何判定垃圾:引用计数法和可达性分析法,以及判断是否是垃圾的步骤
- 简述三个垃圾回收算法:标记清除法、复制算法和标记整理法
- 简述垃圾收集器
简述JVM中四种引用类型
思路:简述引用类型即可,弱引用WeakReference
在ThreadLocal
中的静态内部类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
异常,一般都是递归调用没有退出或循环调用造成的,在内存不变的情况下,减少线程堆栈,可产生更多的线程
Comments NOTHING