【Java面试题1】简单说说JVM堆的内存结构和GC回收流程
文章目录
前言
JVM堆内存结构简述
JVM堆内存结构图
堆初体验
结构详情
新生代
老年代
永久代/元空间
GC回收流程
GC回收流程图
GC回收详细流程
查看JDK自带可视化堆空间图
总结
前言
我们在Java面试中,只要是2年以上经验,一定会问你一下关于JVM方面的问题。开发两年时间不能对JVM没有了解,如果你还不了解,说明你的学习与探索能力不高,不能给到应有的加分项哦!小编也是最近刚刚开始学习,总结一下最近的学习成果。方便以后看,也希望小伙伴们看到可以帮助到您!
JVM堆内存结构简述
JVM堆内存结构图
堆初体验
所有的对象实例以及数组都要在堆上分配,堆是垃圾收集器管理的主要区域,也被称为“GC 堆”,也是我们优化最多考虑的地方。因为在一个项目中,会不断地创建对象,都是在堆里创建,如果一直不回收就会导致OOM,我们听的最多的情况哈!还有经常说的JVM调优,也是对堆进行参数优化配置,达到最接近理想状态。
结构详情
新生代
大部分刚创建的对象首先都是放在年轻代,新生代内存按照 8:1:1 的比例分为一个
Eden 和两个 Survivor
(Survivor from,Survivor to)。
1. Eden 空间
Eden空间:主要是存放刚刚创建的新对象,如果可以Eden空间充足,新对象直接存放在Eden中,如果对象过大,放不下则会触发
Minor GC(效率很快)
。
2. Survivor 空间
每次执行Minor GC,会将Eden区中存活的对象放到Survivor的From区,而在From区中,仍存活的对象会根据他们的年龄值来决定去向,逃过一次Minor GC年龄加1,默认年数为15,就要到老年区。(From Survivor和To Survivor的逻辑关系会在GC时发生颠倒: From变To , To变From,目的是保证有连续的空间存放对方,避免碎片化的发生,后面GC流程在详细说)
老年代
在新生代中经历了 N 次(默认15次)垃圾回收后仍然存活的对象,就会被放到年老代中。年老代中存放的都是一些生命周期较长的对象。当老年代内存满时触发 Major GC 即 Full GC,Full GC 发生频率比较低,执行时间也是Minor GC的十倍以上。在老年代的对象一般为:存活时间比较长的,还有就是比较大的对象。
永久代/元空间
Java8 以前永久代,受JVM 管理,java8 以后元空间,直接使用物理内存。元空间位于堆外
,所以它的最大内存大小取决于系统内存,而不是堆大小,我们可以指定 MaxMetaspaceSize
参数来限定它的最大内存。
GC回收流程
GC回收流程图
Java8 以前永久代,受JVM 管理
GC回收详细流程
当一个新对象创建时,首先会来到新生区的Eden区中,这里进行第一次判断:判断当前新对象是否可以再Eden区放得下,如果放下我们直接放到Eden区分配内存即可;如果放不下时,就要进行一次Minor GC。此次GC我们展开来详细说一下:回收时进行第二次判断:判断Survivor0是否放得下,如果放得下Eden 区存活对象复制到一个 Survivor0 区,然后清空 Eden 区,当这个 Survivor0 区也存放满了时,则将 Eden 区和 Survivor0 区存活对象复制到另一个 Survivor1 区,然后清空 Eden 和这个 Survivor0 区,此时 Survivor0 区是空的,然后将 Survivor0 区和 Survivor1 区交换,即保持 Survivor1 区为空, 如此往复(对照上面两个区域来回切换)。当对象在 Survivor 区躲过一次 GC 的话,其对象年龄便会加 1,此时进行第三次判断:判断年龄是否达到阈值,默认情况下,如果对象年龄达到 15 岁。超过就会移动到老年代中。不超则继续在Survivor。(对照上图虚线框)
执行完Minor GC后,进行第四次判断:判断Eden区是否放得下,如果放得下就进行内存分配,如果放不下默认作为大对象放到老年区。此时进行第五次判断新对象是否在老年区放得下,如果放得下就进行内存分配;如果放不下则进行一次Major GC 即 Full GC(执行时间为Minor GC的10倍多),最后进行第五次判断:判断老年区是否放得下,如果放得下进行内存分配;放不下则直接报异常OOM,此时需要改变堆的内存大小了。
-Xmx:最大堆大小
-Xms:初始堆大小
查看JDK自带可视化堆空间图
1. Win + R 输入jvisualvm
GC回收详细流程
当一个新对象创建时,首先会来到新生区的Eden区中,这里进行第一次判断:判断当前新对象是否可以再Eden区放得下,如果放下我们直接放到Eden区分配内存即可;如果放不下时,就要进行一次Minor GC。此次GC我们展开来详细说一下:回收时进行第二次判断:判断Survivor0是否放得下,如果放得下Eden 区存活对象复制到一个 Survivor0 区,然后清空 Eden 区,当这个 Survivor0 区也存放满了时,则将 Eden 区和 Survivor0 区存活对象复制到另一个 Survivor1 区,然后清空 Eden 和这个 Survivor0 区,此时 Survivor0 区是空的,然后将 Survivor0 区和 Survivor1 区交换,即保持 Survivor1 区为空, 如此往复(对照上面两个区域来回切换)。当对象在 Survivor 区躲过一次 GC 的话,其对象年龄便会加 1,此时进行第三次判断:判断年龄是否达到阈值,默认情况下,如果对象年龄达到 15 岁。超过就会移动到老年代中。不超则继续在Survivor。(对照上图虚线框)
执行完Minor GC后,进行第四次判断:判断Eden区是否放得下,如果放得下就进行内存分配,如果放不下默认作为大对象放到老年区。此时进行第五次判断新对象是否在老年区放得下,如果放得下就进行内存分配;如果放不下则进行一次Major GC 即 Full GC(执行时间为Minor GC的10倍多),最后进行第五次判断:判断老年区是否放得下,如果放得下进行内存分配;放不下则直接报异常OOM,此时需要改变堆的内存大小了。
-Xmx:最大堆大小
-Xms:初始堆大小
查看JDK自带可视化堆空间图
1. Win + R 输入jvisualvm
2. 安装GC插件
3. 查看内存图
总结
这样一个大的面试题就描述完成了,主要是理解GC回收的流程懂了,堆的结构也就知道怎么回事了。