说说:为什么新生代要两个Survivor区? 一个不行吗?
聊聊:JVM内存为什么要分成新生代,老年代,永久代。新生代中为什么要分为Eden和Survivor。
思路:
先讲一下JAVA堆,新生代的划分,再谈谈它们之间的转化,
相互之间一些参数的配置(如:–XX:NewRatio,–XX:SurvivorRatio等),
再解释为什么要这样划分,最好加一点自己的理解。
参考答案:
这样划分的目的:是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
1)共享内存区划分
共享内存区 = 永久代 (/元空间) + 堆 永久代 = 方法区 + 其他 Java堆 = 老年代 + 新生代 新生代 = Eden + S0 + S1
2)一些参数的配置
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配置。 默认的,Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定) Survivor区中的对象被复制次数为15 (对应虚拟机参数 -XX:+MaxTenuringThreshold)
3)为什么要分为Eden和Survivor? 为什么要设置两个Survivor区?
如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。
老年代很快被填满,触发Major GC.
老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,
所以需要分为Eden和Survivor。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,
Survivor的预筛选保证,只有经历最大16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,
经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;
等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)
说说:为什么新生代内存需要有两个Survivor区? 一个不行吗?
思路:
先讲一下JAVA堆,新生代的划分
再讲讲这样做的宏观目标: 避免内存碎片化
最后讲讲做这种分区架构所面临的场景:大部分的新建对象,都是生命周期很短的, 使用两个Survivor区的架构,取得 内存碎片化 和 执行效率的 最佳平衡。
参考答案:
首先,说明一下默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,默认的,Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定) 这个 堆内存的结构。
其次,从避免 内存碎片上来说:
在新生代,因为对象的生命周期比较短,为了避免 内存碎片太多, 使用了 标记 复制算法。
在老生代,因为对象的生命周期比较长,为了提升性能,使用了 性能高的 标记清理 或者 标记整理算法。
标记 复制算法,就需要备用空间, 所以两个Survivor ,一个使用,一个备用
从管理的内存大小上来说:
标记 复制算法 ,空间成本高,需要一块额外的内存空间 作为 复制的备用空间,默认情况下,新时代是30%, 用 30%的 10分之一, 也就是 3% 作为 复制的备用空间
备用大部分时候空闲, 3% 的空间是空闲的, 所以内存块不能太大,
CMS是物理分代,G1是逻辑分代。
而在没有做并发分治模式之前(G1),老生代本身对象生命周期长, 老生代的内存也比价大, 占到60%多,标记 复制算法速度慢, 这么大的内存,不适合使用 标记 复制算法。
不使用 标记 复制算法,也就不需要用 备用空间。
在 做并发分治模式之后(G1),整个内存管理的模式, 已经从大块治理,变成小块治理, G1使用的也是标记复制算法, 本质上,已经可以理解为很多的 轮替备用空间了。如果把那些备用空间理解为 surviver的话,可以理解为,实际上存在N多的surviver区了,只是大家没有用这个名词去命名而已。
最后,总结一下:
因为大部分的新建对象生命周期很短、对象存活率低,用复制算法在回收时的效率会更高,也不会产生内存碎片。
复制算法的代价, 就是需要备用空间,为了不节约过多的内存,就划分了两块雷同大小的内存区域survivor from和survivor to。在每次gc后就会把存活对象给复制到另一个survivor上,而后清空Eden和刚应用过的survivor。
只有在Eden空间快满的时候才会触发 Minor GC 。
而 Eden 空间占新生代的绝大部分,所以 Minor GC 的频率得以降低。
当然,使用两个 Survivor 这种方式我们也付出了一定的代价,如 10% 的空间浪费、复制对象的开销等。
这个标记复制算法,必须付出的代价。
JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代
思路:
先描述一下Java堆内存划分,再解释Minor GC,Major GC,full GC,描述它们之间转化流程。
我的答案:
Java堆 = 老年代 + 新生代 新生代 = Eden + S0 + S1 当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。 大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态; 如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存活的对象进入老年态。 老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和年老代。 Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。
聊聊:什么是分代,分代的必要性
Java 虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代、老年代和永久代(对 HotSpot 虚拟机而言),这就是
JVM 的内存分代策略。
JDK 1.7 之前,Java 虚拟机将堆内存划分为新生代、老年代和永久代(或者元空间),
永久代是 HotSpot 虚拟机特有的概念(JDK1.8 之后为 metaspace 元空间替代永久代),它采用永久代的方式来实现方法区,其他的虚拟机实现没有这一概念,
而且 HotSpot 也有去永久代的趋势,在 JDK 1.7 中 HotSpot 已经开始了“去永久化”,把原本放在永久代的字符串常量池移出。永久代主要存放常量、类信息、静态变量等数据,与垃圾回收关系不大,新生代和老年代是垃圾回收的主要区域。
堆内存是虚拟机管理的内存中最大的一块,也是垃圾回收最频繁的一块区域,我们程序所有的对象实例都存放在堆内存中。
给堆内存分代 是为了提高对象内存分配和垃圾回收的效率。
试想一下,如果堆内存没有区域划分,所有的新创建的对象和生命周期很长的对象放在一起,随着程序的执行,堆内存需要频繁进行垃圾收集,而每次回收都要遍历所有的对象,遍历这些对象所花费的时间代价是巨大的,会严重影响我们的 GC 效率。
有了内存分代,情况就不同了,新创建的对象会在新生代中分配内存,经过多次回收仍然存活下来的对象存放在老年代中,
静态属性、类信息等存放在永久代(或者元空间)中,
新生代中的对象存活时间短,只需要在新生代区域中频繁进行 GC,老年代中对象生命周期长,内存回收的频率相对较低,不需要频繁进行回收,
永久代(或者元空间)中回收效果太差,一般不进行垃圾回收,还可以根据不同年代的特点采用合适的垃圾收集算法。
分代收集大大提升了收集效率,这些都是内存分代带来的好处。
JVM中的永久代中会发生垃圾回收吗
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。
如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。
这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。
硬核面试题推荐
核心面试题目:什么是索引下推?什么是 MRR 优化?怎么才能更好的为表创建索引? 面试重点难题:Mysql如何实现RR级隔离时,不会幻读? 核心面试题:请说说 HashMap 的时间复杂度是多少? 核心面试题: 强引用、软引用、弱引用、虚引用?重点是 各自的 使用场景? 吊打面试官:Java中String对象的大小? 核心面试难题:Java对象为什么 不一定在堆上分配? 核心面试题:MVCC、间隙锁、Undo Log链、表级锁、行级锁、页级锁、共享锁、排它锁、记录锁等等
硬核文章推荐
一文搞懂:Java高手必备之 Mpsc 无锁队列 (史上最全) 一文搞懂:微服务核心组件 Nacos(史上最全) 一文搞懂:微服务核心组件 sentinel(史上最全) 一文秒懂:多级时间轮,最顶尖的Java调度算法 一文搞懂:缓存之王 Caffeine 架构、源码、原理(5W长文) 高性能组件:环形队列、 条带环形队列 Striped-RingBuffer 架构分析 一文穿透:队列之王 Disruptor 原理、架构、源码 如何优雅的使用 单例模式 ?来看看缓存之王 Caffeine 、链路之王 Skywalking 是如何做的吧! 细思极恐:Java官方JVM 为啥要叫做 HotSpot JVM?背后的水,不知道有多深!!! Java核心实操:内存溢出 实战、内存泄漏实战
硬核电子书
本文收录于:《尼恩Java 面试宝典》V17版
长按二维码,点击“识别图中二维码”即可查看老架构师尼恩个人微信,发暗号 “领电子书” 给尼恩,获取最新PDF。
最新的《尼恩Java面试宝典》
极致经典,不断升级,目前最新为V17
尼恩Java高并发三部曲
《Java高并发核心编程-卷1(加强版)》,不断升级
《Java高并发核心编程-卷2(加强版)》,不断升级
《Java高并发核心编程-卷3(加强版)》,不断升级
尼恩架构笔记100篇+,不断添加