JVM
16. 介绍下空间分配担保原则?
如果YougGC时新生代有大量对象存活下来,而 survivor 区放不下了,这时必须转移到老年代中,但这时发现老年代也放不下这些对象了,那怎么处理呢?其实JVM有一个老年代空间分配担保机制来保证对象能够进入老年代。
在执行每次 YoungGC 之前,JVM会先检查老年代最大可用连续空间是否大于新生代所有对象的总大小。因为在极端情况下,可能新生代 YoungGC 后,所有对象都存活下来了,而 survivor 区又放不下,那可能所有对象都要进入老年代了。这个时候如果老年代的可用连续空间是大于新生代所有对象的总大小的,那就可以放心进行 YoungGC。但如果老年代的内存大小是小于新生代对象总大小的,那就有可能老年代空间不够放入新生代所有存活对象,这个时候JVM就会先检查 -XX:HandlePromotionFailure
参数是否允许担保失败,如果允许,就会判断老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次YoungGC,尽快这次YoungGC是有风险的。如果小于,或者 -XX:HandlePromotionFailure
参数不允许担保失败,这时就会进行一次 Full GC。
在允许担保失败并尝试进行YoungGC后,可能会出现三种情况:
① YoungGC后,存活对象小于survivor大小,此时存活对象进入survivor区中
② YoungGC后,存活对象大于survivor大小,但是小于老年大可用空间大小,此时直接进入老年代。
③ YoungGC后,存活对象大于survivor大小,也大于老年大可用空间大小,老年代也放不下这些对象了,此时就会发生“Handle Promotion Failure”,就触发了 Full GC。如果 Full GC后,老年代还是没有足够的空间,此时就会发生OOM内存溢出了。
通过下图来了解空间分配担保原则:
17. 什么是类加载?类加载的过程?
虚拟机把描述类的数据加载到内存里面,并对数据进行校验、解析和初始化,最终变成可以被虚拟机直接使用的class对象;
类的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。如图所示:
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)
类加载过程如下:
-
加载,加载分为三步:
- 1、通过类的全限定性类名获取该类的二进制流;
- 2、将该二进制流的静态存储结构转为方法区的运行时数据结构;
- 3、在堆中为该类生成一个class对象;
-
验证:验证该class文件中的字节流信息复合虚拟机的要求,不会威胁到jvm的安全;
-
准备:为class对象的静态变量分配内存,初始化其初始值;
-
解析:该阶段主要完成符号引用转化成直接引用;
-
初始化:到了初始化阶段,才开始执行类中定义的java代码;初始化阶段是调用类构造器的过程;
18. 什么是类加载器,常见的类加载器有哪些?
类加载器是指:通过一个类的全限定性类名获取该类的二进制字节流叫做类加载器;类加载器分为以下四种:
-
启动类加载器(
BootStrapClassLoader
):用来加载java核心类库,无法被java程序直接引用; -
扩展类加载器(
Extension ClassLoader
):用来加载java的扩展库,java的虚拟机实现会提供一个扩展库目录,该类加载器在扩展库目录里面查找并加载java类; -
系统类加载器(
AppClassLoader
):它根据java的类路径来加载类,一般来说,java应用的类都是通过它来加载的; -
自定义类加载器:由java语言实现,继承自
ClassLoader
;
19. 什么是双亲委派模型?为什么需要双亲委派模型?
当一个类加载器收到一个类加载的请求,他首先不会尝试自己去加载,而是将这个请求委派给父类加载器去加载,只有父类加载器在自己的搜索范围类查找不到给类时,子加载器才会尝试自己去加载该类;
为了防止内存中出现多个相同的字节码;因为如果没有双亲委派的话,用户就可以自己定义一个java.lang.String类,那么就无法保证类的唯一性。
补充:那怎么打破双亲委派模型?
自定义类加载器,继承ClassLoader
类,重写loadClass
方法和findClass
方法。
20. 列举一些你知道的打破双亲委派机制的例子,为什么要打破?
-
JNDI 通过引入线程上下文类加载器,可以在
Thread.setContextClassLoader
方法设置,默认是应用程序类加载器,来加载 SPI 的代码。有了线程上下文类加载器,就可以完成父类加载器请求子类加载器完成类加载的行为。打破的原因,是为了 JNDI 服务的类加载器是启动器类加载,为了完成高级类加载器请求子类加载器(即上文中的线程上下文加载器)加载类。 -
Tomcat,应用的类加载器优先自行加载应用目录下的 class,并不是先委派给父加载器,加载不了才委派给父加载器。
tomcat之所以造了一堆自己的classloader,大致是出于下面三类目的:
-
对于各个
webapp
中的class
和lib
,需要相互隔离,不能出现一个应用中加载的类库会影响另一个应用的情况,而对于许多应用,需要有共享的lib以便不浪费资源。 -
与
jvm
一样的安全性问题。使用单独的classloader
去装载tomcat
自身的类库,以免其他恶意或无意的破坏; -
热部署。
-
tomcat类加载器如下图:
-
OSGi,实现模块化热部署,为每个模块都自定义了类加载器,需要更换模块时,模块与类加载器一起更换。其类加载的过程中,有平级的类加载器加载行为。打破的原因是为了实现模块热替换。
-
JDK 9,Extension ClassLoader
被Platform ClassLoader
取代,当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载。打破的原因,是为了添加模块化的特性。
-
21. 说一下 JVM 调优的命令?
jps
:JVM Process Status Tool
,显示指定系统内所有的HotSpot虚拟机进程。jstat
:jstat(JVM statistics Monitoring)
是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。jmap
:jmap(JVM Memory Map)
命令用于生成heap dump文件,如果不使用这个命令,还阔以使用-XX:+HeapDumpOnOutOfMemoryError
参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。jhat
:jhat(JVM Heap Analysis Tool)
命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。jstack
:jstack
用于生成java虚拟机当前时刻的线程快照。jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
22. Java对象创建过程
- JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲)
- 为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分配(TLAB)”
- 将除对象头外的对象内存空间初始化为0
- 对对象头进行必要设置
23. JDK新特性
JDK8
支持 Lamda
表达式、集合的 stream
操作、提升HashMap
性能
JDK9
//Stream API中iterate方法的新重载方法,可以指定什么时候结束迭代
IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
默认G1垃圾回收器
JDK10
其重点在于通过完全GC并行来改善G1最坏情况的等待时间。
JDK11
ZGC (并发回收的策略) 4TB
用于 Lambda 参数的局部变量语法
JDK12
Shenandoah GC (GC 算法)停顿时间和堆的大小没有任何关系,并行关注停顿响应时间。
JDK13
增加ZGC以将未使用的堆内存返回给操作系统,16TB
JDK14
删除cms垃圾回收器、弃用ParallelScavenge+SerialOldGC
垃圾回收算法组合
将ZGC垃圾回收器应用到macOS和windows平台
总结:JVM部分的面试,经过三篇文章成功更新完成,在此感谢各位小伙伴支持,点个关注不迷路。谢谢!!
如果没看过前几篇的文章的,可以点击链接去看看,如果方便可以点赞收藏,给我点个关注,创作不易!