【JVM】内存分配与回收概念详解与实例

Java自动内存管理的最根本目标是自动化地解决两个问题:

1、自动给对象分配内存

2、自动回收分配给对象的内存

一、虚拟机中的垃圾收集器判断对象是否存活以及回收的部分算法分析

什么时候需要对内存动态分配和内存回收进行监控和调节呢?

当我们需要排查各种内存溢出、内存泄漏问题时,以及垃圾收集成为系统达到更高并发量瓶颈的时候。

内存分配与回收的讨论主要指Java堆及方法区(这里是保存系统类信息,比如类的字段、方法等的地方)的内存管理。因为它们中的内存并不是随着线程结束而回收的。

1.虚拟机中判断对象是否存活的算法

引用计数算法

在对象中添加一个引用计数器,每当有一个地方引用它时计数器的值加一,引用失效则减一。

优势:原理简单,并且高效,Python中就使用了这一算法

劣势:不配合其它额外处理无法解决对象之间互相引用等问题,如以下代码:

上面的两个对象已经不会再使用,然而它们之间存在相互引用,引用计数器显然不为0

可达性分析算法

Java中使用的正是这种算法,目前主流的商用程序语言如Java,C#等的内存管理都采用可达性分析算法。

思路:以某一些可以作为“GC Roots”的根对象来作为起始节点,根据引用关系从它们开始向下搜索,过程中走过的路径称为“引用链”,如果对象到根对象间没有任何引用链相连即根对象不可达此对象时,证明该对象不会再被利用

这样的话,即使几个对象之间互有关联,但是都没有与根对象建立“引用链”的关系,那这些对象都是会被判定为可回收的。

其实在确定没有关联之后对象只是被标记,如果在标记期间依然建立起关联,那么可以不被回收(如与某个类建立联系)。

可以作为根对象的对象:局部变量表(存储方法的参数和局部变量、临时变量等)中引用的对象、类的静态属性引用的对象、常量引用的对象等,这里不详细例举完所有。

说了引用对象,这里也说一下四种引用类型

强引用:比如Object obj = new Object(),只要这种关系存在,就永远不会回收这个对象

软引用(针对有用但是非必须的对象):这种引用关系的对象在内存将溢出以前会被列入回收范围

弱引用:被弱引用的对象只能存活到下一次垃圾回收

虚引用:只是用来确保对象被回收时会收到系统通知。不会对生存时间产生影响,也无法通过虚引用取得对象。

2.垃圾收集算法

分代收集的理论假说

(1)绝大多数对象都会很快消亡

(2)在越多次垃圾回收中存活下来的对象越难消亡

这里提出这样的假说是为了更好地说明下面的垃圾收集器设计原则(对于多款常用垃圾收集器)

Java堆会被划分为不同区域,根据对象度过的垃圾回收次数(可以简单理解为年龄)分配到不同空间进行存储。

如此一来,每次回收只需要考虑存活的对象,其余大量的空间就可以以很低的时间成本回收到。

然而还没有解释,分代指的是什么呢?

一般来说,Java堆中会被划分为新生代与老年代。简单来说,新生代中的对象存活时间较老年代是很短的,每次回收只会存活少量对象,而剩下的存活对象,就会被放到老年代中去。

不同算法中新生代与老年代划分方式不同,具体可以见下文对算法的分析,这里先说一下需要讲到的内存碎片的概念。

内存碎片

提到回收空间就不得不说内存碎片,什么是内存碎片呢?

比如我有一段存储空间可以存8个字节,现在给其中一些分配要存放的东西。

|1 2 3 4 5 6 - -|

然后其中一些被我删除了

|- - - 4 5 6 - -|

之后我又需要存放一些东西了,这些东西需要占五个字节,可是现在明明有五个字节的内存,却因为没有五个字节这么大的空间而放不下了。

像这样的情况就可以称为内存碎片。

标记--清除算法

先标记需要回收的对象,再对它们进行回收。也可以反过来,标记不用回收的对象。

最基础的收集算法,缺点也很明显:会因为Java堆中对象数量的增长而降低效率,并且会产生大量的内存碎片。

标记--复制算法

将内存空间分为不同区域,每次只使用其中一些区域,这一块的内存用完了,就把存活对象复制到没有使用的区域(这样避免了内存碎片)。就这样说可能不明白,下面我画了一张图:

绿色表示存活对象,蓝色表示需要回收的对象,黄色表示没有使用的区域

目前的商用Java虚拟机大多采用这样的算法。

以上就是判断对象是否存活以及回收的部分算法分析

二、分配内存

分配的基本方式

在经典分代的设计下,新对象一般会分配给新生代,只有类似对象大小超过一定阈值的情况才会直接分配到老年代

对象一般是优先在新生代分配的,然而也会存在特例。当需要大量连续内存空间的对象出现时(很长的字符串或元素很多的数组等),为了避免反复进行内存复制(因为大对象占了太多空间),就会直接在老年代分配内存。

长期存活的对象会被放在老年代,当对象的“年龄”超过了某个阈值,它就可以晋升老年代。当然这个阈值也存在特例,根据对象所占内存与年龄进行一定的判定后,也会放入老年代。

空间分配担保

当新生代空间不够用时,比如出现了新生代中所有对象都存活了的这种极端情况,此时就需要老年代来进行分配担保,把无法容纳的对象送入老年代(前提是老年代空间足够)。为什么说是担保呢?这种形式与生活中,向银行贷款时的信贷公司的作用是相同的,这里老年代作一个担保,确保可以进行空间分配。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值