JVM一些面试题目探究
在Java面试中,在考察完项目经验、基础技术后,我会根据候选人的特点进行知识深度的考察,如果候选人简历上有写JVM(Java虚拟机)相关的东西,那么我常常会问一些JVM的问题。
-
请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?
Java Virtual Mavhine :
从Java平台的逻辑结构上来看,JVM包含了Java Hotspot Client VM和Java Hotspot Server VM两个模块(JDK包含JRE,JRE包含JVM的逻辑结构);
从JVM自身的物理结构上来看,Class文件通过类加载子系统进入内存空间(方法区、Java堆、Java栈、本地方法栈),然后方法区地址的调用和内存空间数据和指令执行,本地方法栈可以通过本地方法口调用本地方法库,最后是垃圾的收集。
它有几个部分最重要:
- java代码编译和执行的整过过程。
- JVM内存管理及垃圾回收机制。
变化更新:
-
HashMap底层结构的改变
jdk1.7 HashMap: 数组+单向链表
jdk1.8 HashMap: 数组+(单向链表/红黑树)
-
删除永久区(PermGen),引入元空间(Metaspace)
jdk1.6版本中,方法区为永久代,这样GC工作区域就可以扩展至方法区。这种策略可以避免为方法区单独设计垃圾回收机制,但是坏处就是,方法区的回收条件十分苛刻,而且回收效果也不好。
jdk1.7版本中,只将方法区中的字符串常量池移除永久代。
jdk1.8版本中,删除永久代区,用元空间来代替原来的永久区元空间内的规则:元空间中类及其相关的元数据和类加载器生命周期一致,每个类加载器有专门的存储空间,不会单独回收某个类,位置也是固定的,但是当类加载器不再存活时会把它相关的空间全部移除。
两者区别在于:元空间并不在虚拟机中,而是使用本地内存(物理内存)
-
Lambda表达式
jdk1.8版本中,Lambda表达式的实现方式本质是以匿名内部类的形式实现的
函数式接口:Lambda表达式专门针对只有一个方法的接口
基本格式:(x,y…)->{表达式…};
- java代码编译和执行的整过过程:
过程流程为:源代码–>词法分析器–>Token流–>语法分析器–>语法树/抽象语法树–>语义分析器–>注解抽象语法树–>字节码生成器–>JVM字节码,开始调用执行引擎;
JVM字节码–>机器无关优化–>中间代码–>机器无关优化–>中间代码–>寄存器分配器–>中间代码–>目标代码生成器–>目标代码
包含三个重要的机制:
-
Java源码编译机制
由以下三个过程组成:
-
分析和输入到符号表
-
注解处理
-
语义分析和生成Class文件
由以下部分组成:
- 结构信息
- 元数据
- 方法信息
-
-
类加载机制
类加载是通过ClassLoader及其子类来完成:
1)Bootstrap ClassLoader
2)Extension ClassLoader
3)App ClassLoader
4)Custom ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,加载的顺序是自顶向下
-
类执行机制
-
什么是OOM,什么是栈溢出StackOverFlowError?怎么分析?
全称:Out Of Memory–>内存溢出、内存不够
包来源:java.lang.OutOfMemoryError
官方说明:当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出error(注:非exception,因为这个问题已经严重到不足以被应用处理)
StackOverFlowError异常:
堆栈空间用尽产生的异常。
- 一般产生原因:出现无限递归或死循环,局部变量不停的创建
- 处理方法:适用jstack输出.out文件,然后分析每个线程栈的运行情况;也可以通过虚拟机参数-Xss来设置栈的大小
分析,常见的OOM三种情况:
- Java heap space–>堆内存溢出
- PermGen space–>永久代溢出
- StackOverflowError–>栈溢出
-
JVM的常用调优参数有哪些?
-
堆设置:
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。
-XX:MaxPermSize=n:设置持久代大小
-
收集器设置:
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParallelGCOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并行收集器
-
垃圾回收统计信息:
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
-
并行收集器设置:
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRation=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
-
并发收集器设置:
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
-XX:PretenureSize Threshold=3145728
-XX:MaxTenuringThreshold=15
-XX:-DisableExplicirGC(+表示是,-表示否)
-
-
内存快照如何抓取,怎么分析Dump文件?
- 确认gdb软件包已经被正确安装。
- 使用调试参数编译程序(例如: gcc中使用"-g"选项),编译后不要去除文件的调试符号信息。
- 执行应用程序。
- 执行gcore命令生成指定应用程序的core文件并且保存在当前目录下。 $ gcore pid (进程号)
分析Dump文件:
使用jvisualvm来分析dump文件,jvisualvm是JDK自带的Java性能分析工具,在JDK的bin目录下,文件名就叫jvisualvm.exe。使用方式:直接双击打开jvisualvm.exe,点击文件->装入,在文件类型那一栏选择堆,选择要分析的dump文件,打开。
jvisualvm监控:
分析(本地设置的最大堆内存是800M左右):红框框出的部分是发生堆内存溢出时的情形,已使用的堆大小(蓝色部分)并没有增长特别明显,但是申请的堆的大小(黄色部分)从默认的400多兆急速上涨,涨到800M,然后内存溢出,然而使用的堆大小依然没怎么增长。
所以,dump文件中的实例列表其实是反映了使用的堆的情况,而使用的堆内存并没有达到预先设置的最大堆内存,只是在申请堆内存的过程中超出了预先设置的最大堆内存,然后内存溢出。
-
谈谈JVM中,类加载器你的认识?
VM将指定的class文件读取到内存里,并运行该class文件里的Java程序的过程,就称之为类的加载,类的加载是需要类加载器完成的。类加载的最佳时机是解析Java字节码类文件中常量池符号的时候,Class.forName()、ClassLoader.loadClass()、反射API和JNI_FindClass都可以触发类加载,Hot JVM自身启动的时候也会触发类加载。
类加载器在JVM中的作用有:
- 将类的字节码文件从JVM外部加载到内存中
- 确定一个类的唯一性
- 提供隔离特性,为中间件开发者提供便利,例如Tomcat
补:
类的生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。