内存分配与回收策略

举报
SHQ5785 发表于 2024/06/25 10:00:54 2024/06/25
【摘要】 一、前言在前期博文《JVM进阶(二)——初识JAVA堆》中讲解了虚拟机中的堆,堆是整个内存模型中占用最大的一部分,而且不是连续的。当有需要分配内存的时候,一般有两个方法分配,指针碰撞和空闲列表。该部分的内存回收是由虚拟机的垃圾收集器GC进行管理的。刚刚粗略的回忆了上一博文所讲的内容,而这一章我们的重点还在堆上面。之前有简单的提到过新生代和老年代,今天就给大家好好梳理下这部分。 二、堆类型堆...

一、前言

在前期博文《JVM进阶(二)——初识JAVA堆》中讲解了虚拟机中的堆,堆是整个内存模型中占用最大的一部分,而且不是连续的。当有需要分配内存的时候,一般有两个方法分配,指针碰撞空闲列表。该部分的内存回收是由虚拟机的垃圾收集器GC进行管理的。

刚刚粗略的回忆了上一博文所讲的内容,而这一章我们的重点还在堆上面。之前有简单的提到过新生代和老年代,今天就给大家好好梳理下这部分。

二、堆类型

堆是存放对象以及数组的区域,但不是胡乱的有空间就分配的内存。堆在内存中分为了年轻代年老代

我们先看看年轻代,这个区域又被分为了一个Eden和两个Survivor区,即伊甸园存活区。看下面这张图:

从图中可以清楚的看到他们的关系是8:1。那为什么Eden占用这么多呢?因为对象都会在Eden区创建。每次只使用Eden区和一个Survivor区,当这两个区满了之后就会将还存活的对象复制到另一个空白区(MINOR GC),大家是不是在想那空间怎么会够用呢?其实年轻代的对象有98%都是朝生夕死的,所以根本不用担心不够用,这也是为什么比例是8:1而不是1:1的原因。而且!就算是不够用,我们不是还有年老代吗!

我们暂且先不说年老代,还有个问题没有解决,刚刚我们提到了复制,所以这里我们抱着求知的欲望来讲讲是怎么个复制法。首先看下图:

上图中分为了两个部分,每次只使用其中的一部分(这里不是完全按照刚刚伊甸园和空白区的占用比例来讲,可以理解为通用版)。当这部分满了后,就会将还存活的复制到另一个区,再将这个区清空,如图:

但这种方法也有弊端,就是会浪费了一半的内存空间。但是对于年轻代这种朝生夕死的特征是一个很好的解决方法,因为只要对一半的空间进行操作,把范围大大的缩小了。

对于年老区域,刚刚也说了,如果年轻代不够放了就放在年老代,还有一种情况就是对象在年轻代中存活的太久了,就会放到年老区,就像人的岁数大了就会变老年人,对象在年轻代也有岁数:每当进行一次复制回收的时候,还在年轻代中存活的对象就会加1岁,默认15岁后就到年老代。可以通过-XX:MaxTenuringThreshold=15来设置多少岁后进入年老区。

三、GC 方法

年老和永久区垃圾收集的方法都是“标记-清除-整理”,看下图:

这里如果还使用年轻代的回收方法的话肯定不适用了,那边的特性是朝生夕死,而年老代存活的一般是大对象或者很难死去的对象(回收),所以不符合条件。当年老代内存不足的话就会触发垃圾收集,这个回收叫做FULL GC。默认是占用了68%后收集,可用参数-XX:CMSInitiatingOccupancyFraction=68自行设置。

收集方法中的标记这里先不说,标记好了就清除掉,最后整理成逻辑连续的区域。最后的结果图如下:

这样可以有效的避免了内存碎片。

OK,这章大致就先这样吧,留下了个问题,在下一章我们会讲解什么是标记,怎么标记的?

四、拓展阅读

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。