【面试题总结三】JVM部分

前言

面试题总结这个部分分为七个部分,第一部分是JAVA基础,第二部分是并发控制,然后第三部分是JVM,第四部分是框架(Spring/SpringMVC/SpringBoot),第五部分是数据库(MySQL/Redis,我只会这两,SQL Server应该没人用,Oralce只会增删改查)。第六部分是分布式和微服务第七部分是高性能IO和RPC,大概也就是这些了,网络和操作系统不打算拿出来总结了,因为这几个方面的问题基本上都不会很为难面试者,当然这里指的是后端开发,其他的我不了解。至于数据结构和算法,这个emmm,多刷刷题。

推荐

其实关于虚拟机的问题,深入JAVA虚拟机,这里面有几篇博文基本上涵盖了。这里只把一些常见的问题拿出来看看。

运行时内存

从GC的角度来看,JVM在运行的时候会把堆划分为两大区域:
在这里插入图片描述大小和比例都在图上了,通过这些参数,可以调整他们的大小
(1)-Xms:这个规定了虚拟机启动的时候堆的初始大小
(2)-Xmx:这个规定了虚拟机堆的最大size
(3)-Xmn:这个规定了堆内存中,新生代的内存大小。因为虚拟机内存一般是分代的,分为新生代和老年代,其中每个代内又有不同的分区。
(4)-XX:SurivivorRatio:这个规定了新生代中,Eden区和Surrvivor的比例,是8:1:1。
(5)-XX PrintGCDetails:打印GC日志。

新生代和老年代存放的对象区别

新生代主要是存一些小的,生命周期比较短的对象,老年代存放存放生命周期比较长的对象

常用的垃圾回收算法

标记整理,复制算法,标记清除等。

MinorGC什么时候回触发

在Eden区满的时候,MinorGC会触发。

发生GC后,依然存活的对象的变化

GC发生后,被扫描的对象年龄会+1,年龄达到一定的值会被移动到老年代。

OOM会发生在哪里

(1)老年代内存不够时,会发生OOM(这里有一个点就是,当对象第一次被创建的时候,如果Eden区装不下,或者超过了Eden区的规定的比例,这个对象会被直接丢到老年代,当老年代也装不下就会OOM)
(2)还有一个地方时永久代,这个地方主要存一些类信息和元数据,这里装满时,也会OOM。

如何判断对象是否可以回收

参见对象生死的判定

强引用,弱引用,软引用

(1)强引用可以理解为直接赋值,被强引用的对象只要处于可达状态,就不会被垃圾回收。
在这里插入图片描述
比如这段代码,list永远存在,因为它用了a,a在会被丢入常量池。所以a一直可达。
(2)软引用,采用SoftReference实现
在这里插入图片描述
这个比较灵活,虚拟机内存够不会回收,不够了就会回收。
(3)弱引用:弱引用需要用 WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够,总会回收该对象占用的内存。
通过WeakReference实现:
在这里插入图片描述# 垃圾收集器都有哪些
(1)Serial 垃圾收集器,单线程,采用复制算法,它工作时,其他一切线程停止。
(2)ParNew 垃圾收集器,多线程版的Serial收集器,线程数等于CPU数,通过-XX:ParallelGCThreads指定。
(3)Serial Old 收集器,和Serial类似,单线程,但是主要的区别就是收集算法采用标记整理,因为老年代的对象都是一些大的对象,标记复制的效率会慢一点。对应的还有一个多线程版本的ParOld收集器。
(4)CMS这个没怎么研究
(5)G1,最新的收集器,标记整理算法实现的,低停顿,不产生内存碎片。

类加载的过程

参见类加载

为什么需要双亲委派机制

严格来说双亲委派加载机制只是一种推荐方式,并不是必须的。双亲委派机制说的是在加载类的时候,优先委托自己的父加载器去加载它,这样做的好处是,可以避免重复加载一个类。因为类在虚拟机的唯一定位是根据类本身的信息和类加载器共同决定的。比如A类,使用拓展类加载器和应用程序类加载器来加载,会被认为是两个类。

什么时候需要打破双亲委派模型

出现这个问题的主要原因就是父亲类加载器加载的类去调用子类加载器加载的类,这个有一个十分经典的例子:jdbc驱动的加载
在连接数据库时,需要加载对应的驱动。但是这个驱动,每种数据库厂商都有自己的实现,所以jdk中没有这些驱动,我们需要自己添加jar包。
然而数据库驱动管理器:DriverManager,这个类是需要被启动类加载器加载的,在这个类里面需要使用子类加载的驱动,这就需要打破双亲委派模型。至于怎么做到,其实不难,自己定义一下类加载的过程,不向向上递归式的委派,尝试自己优先加载。

常量池技术

(1)String a=new String(“a”),会创建几个对象?答案是一个或者两个,其实String在创建的时候,首先会在常量池里面创建一个字符串常量,然后再在堆上创建,如果查找常量池已经有了,那么就只在堆上创建。
(2)基本数据类型和对应的包装类,都实现了常量池技术,具体点就是在范围[-128,127]之间的数据,在常量池上有缓存,在这个范围赋值的时候,会直接从常量池中取。这个感觉不怎么常问,也就见过一次。

JVM调优的几个策略

(1)尽可能把对象放在新生代,因为老年代满了会触发Full gc,Full gc的成本要比Minor gc高很多。
(2)大的对象直接进入老年代,这个可通过对应的虚拟机参数指定。因为在新生代经常Minor gc,会把对象复制来复制去的,这对大对象来说很费时。
(3)合理设置进入老年代的年龄限制。
基本就是这些了,其他的想起来再补

发布了371 篇原创文章 · 获赞 49 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/104356060