Java岗大厂面试百日冲刺【Day35】—— JVM2
本文已获得原作者 _陈哈哈 授权并经过重新整理规划后发布。
本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识、集合容器、并发编程、JVM、Spring全家桶、MyBatis等ORMapping框架、MySQL数据库、Redis缓存、RabbitMQ消息队列、Linux操作技巧等。
地球人都知道,Java有个东西叫垃圾收集器(GC),它让创建的对象不需要像c/c++那样delete、free掉,你能不能谈谈,GC是在什么时候,对什么东西,做了什么事情?
这个是经典抛砖引玉的问法,既能看出我们对GC掌握的情况,也能从多点切入深问,看着三句话,其实每句话的知识点都够我们喝一壶的。JVM接下来三篇会以:什么时候进行GC
、对哪些对象
、做哪些处理
三个角度的面试问题进行整理,来吧,卷。
本篇的内容主要基于在什么时候进行GC
这个方向来学习。
面试题1:简单说一下java的垃圾回收机制。
任何语言在运行过程中都会创建对象,也就意味着需要在内存中为这些对象在内存中分配空间,如果这些对象只增加不减少,那么堆空间很快就会被耗尽。因此在这些对象失去使用的意义的时候,需要释放掉这些内容
,保证内存能够提供给新的对象使用,对于对象内存的释放就是垃圾回收机制
,也叫做gc(Garbage Collection,GC)。
对于java开发者来说gc是一个双刃剑,像C语言的垃圾回收是人工的,工作量大,但是可控性高。而java是自动化的,但是可控性很差,甚至有时会出现内存溢出的情况
,内存溢出也就是jvm分配的内存中对象过多,超出配置的JDK最大可分配内存的大小
。
面试题2:JVM会在什么时候进行GC呢?
JVM常在以下几种场景时进行GC操作:
- 在cpu空闲的时候自动进行回收
- 主动调用System.gc()后尝试进行回收,是否回收由JVM决定。
Eden区域满了,或者新创建的对象大小 > Eden所剩空间
,执行Minor GC
。升到老年代的对象大于老年代剩余空间的时候执行Full GC
,或者Young GC中发生promotion failure强制Full GC
。- 在堆内存存储满了之后进行GC,如gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM
YGC出现promotion failure的场景
: promotion failure发生在Young GC, 如果Survivor区当中存活对象的年龄达到了设定值,会就将Survivor区当中的对象拷贝到老年代,如果老年代的空间不足,就会发生promotion failure, 强制进行Full GC 。
追问1:介绍一下不同代空间的垃圾回收机制
新生代(Young generation):
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC
,因为 Java 对象大多都具备朝生夕灭(很快不再使用)的特性,所以 Minor GC 非常频繁
,一般回收速度也比较快。这一定义既清晰又易于理解。。
老年代(Old generation):
对象没有变得不可达,并且从新生代周期中存活了下来,会被拷贝到这里。其区域分配的空间要比新生代多
。也正由于其相对大的空间,发生在老年代的GC次数要比新生代少得多。清理老年代内存一般直接是 Full GC
来清理。
默认的新生代(Young generation)、老年代(Old generation)所占空间比例为 1 : 2 。
持久代(Permanent generation):
也称之为方法区(Method area)
:用于保存类常量以及字符串常量
。注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC。发生在这个区域的GC事件为 Major GC
。
出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10倍以上。只不过在这个区域发生GC的条件非常严苛,必须符合以下三种条件才会被回收:
- 所有实例被回收
- 加载该类的ClassLoader 被回收
- Class 对象无法通过任何途径访问(包括反射)
追问2:能说一下新生代空间的构成与执行逻辑么?
新生代(Young generation)用来保存那些第一次被创建的对象,它被分成三个空间:
- 一个伊甸园空间(Eden)
- 两个幸存者空间(From Survivor、To Survivor)
默认新生代空间的分配:Eden : From : To =8 : 1 : 1
每个空间的执行顺序如下:
- 绝大多数
刚刚被创建的对象
会存放在伊甸园空间(Eden)
。 - 在伊甸园空间执行第一次GC(Minor GC)之后,存活的对象被移动到其中一个幸存者空间(Survivor)。
- 此后,每次伊甸园空间执行GC后,
存活的对象
会被堆积在同一个幸存者空间。 - 当一个幸存者空间饱和,
还在存活的对象
会被移动到另一个幸存者空间。然后会清空已经饱和的那个幸存者空间
,可见,两个幸存者空间,必须有一个是保持空的。 - 在以上步骤中重复N次(N = MaxTenuringThreshold(
年龄阀值设定,默认15
))依然存活的对象,就会被移动到老年代。
当然,也有例外出现,对于一些比较大的对象(需要分配一块比较大的连续内存空间)则直接进入到老年代。一般在Survivor 空间不足的情况下发生。
追问3:说一下发生OOM时,垃圾回收机制的执行流程。
- 对于一个很大的对象或数组,我们会首先在Eden 尝试创建,如果Eden区内存不够,创建不了,则触发Minor GC;
- Minor GC完成后继续尝试在Eden区存放,发现仍然放不下;
- 尝试直接进入老年代,老年代也放不下
- 触发
FULL GC
清理老年代的空间 - FULL GC完成后尝试往老年代里放,还是放不下
- OOM
面试题3:Full GC 、Major GC和 Minor GC有什么不同
我们见过很多 GC 名词如:Minor GC、Young GC、Full GC、Old GC、Major GC、Mixed GC等。那么这么多GC如何进行大致区分?下面我们引用 R 大在知乎上的回答:
针对 HotSpot VM 的实现,它里面的 GC 其实准确分类有两种:
1.Partial GC(局部 GC):
并不收集整个 GC 堆的模式
- Young GC: 只收集 Young Gen 的 GC,Young GC 还有种说法就叫做 Minor GC,两者是一个意思;
- Old GC: 只收集 old gen 的 GC,只有垃圾收集器 CMS 的 concurrent collection 是这个模式;
- Mixed GC: 收集整个 Young Gen 以及部分 old gen 的 GC,只有垃圾收集器 G1 有这个模式;
2.Full GC(全局 GC):
收集整个堆,包括新生代,老年代,永久代(在 JDK 1.8 及以后,永久代被移除,换为 metaspace 元空间)等所有部分的模式;
接下来让我们再来了解下各个 GC:
(1)Minor GC / Young GC
首先我们先来看下 Minor GC / Young GC,大家都知道,新生代(Young Gen)也可以称之为年轻代,这两个名词是等价的。那么在年轻代中的 Eden 内存区域被占满之后,实际上就需要触发年轻代的 GC,或者是新生代的 GC。
其实就是所谓的 Minor GC
,也可以称之为 Young GC
。
(2)Old GC
所谓的老年代 GC,称之为 Old GC 更加合适一些,因为从字面意义上就可以理解,这就是所谓的老年代 GC。
(3)Full GC
对于 Full GC,可以说 Full GC 指的是针对新生代、老年代、永久代的全体内存空间的垃圾回收
,所以称之为 Full GC。
(4)Major GC
上面我们提到,Major GC用于处理方法区的对象。这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC,发生概率很低。
(5)Mixed GC
Mixed GC 是 G1 中特有的概念,其实说白了,主要就是说在 G1 中,一旦老年代占据堆内存的 45%(-XX:InitiatingHeapOccupancyPercent:设置触发标记周期的 Java 堆占用率阈值,默认值是 45%
。这里的Java 堆占比指的是 non_young_capacity_bytes,包括 old + humongous),就要触发 Mixed GC,此时对年轻代和老年代都会进行回收。Mixed GC 只有 G1 中才会出现
。
评论区