JVM(十一):内存分配

JVM(十一):内存分配

在前面的章节中,我们花了大量的篇幅去介绍 JVM 内的内存布局、对象在内存中的状态、垃圾回收的算法和具体实现等。今天让我们探讨一下对象是如何分配内存的。

堆内存划分

前面说过,从内存回收的角度来看,堆被分为以下几个部分:

堆内存划分

开始创建的对象大多都会直接分配到新生代一块区域中,只有大对象和经过多次 GC 后依然存活的对象会放置在老年代中。

那么一个对象在被创建出来后,何时存在与哪个内存区域中呢,其具体的分配策略又是什么呢?下面就让我们一起来看一下几个具体的内存分配策略,了解一个对象在产生后该何去何从。

对象优先在Eden分配

首先,我们先复习一下 GC 的两种分类:

Minor GC,又叫新生代GC:发生在新生代的 GC,由于大多数 Java 对象都是朝生夕死的,因此发生这种 GC 十分的频繁,回收速度一般也会比较快。
Full/Major GC,又叫老年代GC:发生在老年代的 GC,一般会比 Minor GC 慢 10倍以上。

在大多数情况下,对象优先在 Eden 区进行分配。当 Eden 区空间不足时,将会发起一次 Minor GC。

大对象直接进入老年代

首先,我们来看一下什么是大对象,其一般是指需要大量连续内存空间的 Java 对象。最典型的就是很长的字符串和数组。对虚拟机来说大对象是很难处理的对象,因为虚拟机需要一块连续的大空间进行分配,不过更加难以处理的是朝生夕死的大对象,经常出现的大对象,导致频繁地触发 GC 来空出大量连续空间来安置它们,影响了性能。因此,这种情况也警醒开发人员在开发过程中要尽量避免这种问题。

长期存活对象进入老年代

因为 Java 的垃圾回收是采取的分代收集思想。因此新生代的对象也有一定的途径进入老年代,对应的就是存活时间,为了定位每个对象的存活时间,JVM 给每个对象定义了一个对象年龄计数器,当对象在 Survivor 区每熬过一次 Minor GC,对象的年龄就 +1,当对象的年龄满足一定的条件(默认为15),就将其晋升到老年代。

前面这种晋升老年代的条件显得比较的死板。因此在 JVM 中还有一种灵活的方式:如果在 Survivor 空间中相同年龄的所有对象综合大于 Survivor 空间的一半,那么年龄大于等于该阀值的对象就可以直接进入老年代,而无须等到满足要求的年龄。

空间分配担保

上面说过,在新生代经过 Minor GC 存活的对象,满足一定的条件会进入老年代 , 因此就需要保证老年代有充足的空间能够分配 。这种担保方式让 JVM 在进行 Full GC 的时候,可以进行大胆的操作 (因为 Full GC 十分的耗时,影响性能, 因此在 Minor GC 前需要进行一定的判断)。

空间分配担保

上图就是空间分配担保的流程,此种方式可以避免 Full GC 过于频繁,影响性能。

总结

在本文中,我们详细讲述了对象内存分配的一些通用策略。了解这些策略可以让我们明白 JVM 分配对象的逻辑,写出更加高效健壮的代码。

iceWang公众号

文章在公众号「iceWang」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!

本系列文章主要借鉴自《深入分析 JavaWeb 技术内幕》和《深入理解 Java 虚拟机-JVM 高级特性与最佳实践》。

posted @ 2019-08-20 20:41  iceWang丶  阅读(444)  评论(0编辑  收藏  举报