计算机组成原理 常用开发语言的垃圾回收(GC)机制

一、垃圾回收的三种基本方式

1、基本定义

(1)垃圾的定义

        所谓垃圾(Garbage),就是需要回收的对象。作为编写程序的人,是可以做出“这个对象已经不再需要了”这样的判断,但计算机是做不到的。因此,如果程序(通过某个变量等等)可能会直接或间接地引用一个对象,那么这个对象就被视为“存活”;与之相反,已经引用不到的对象被视为“死亡”。将这些“死亡”对象找出来,然后作为垃圾进行回收,这就是GC的本质。

(2)Root

        所谓根(Root),就是判断对象是否可被引用的起始点。至于哪里才是根,不同的语言和编译器都有不同的规定,但基本上是将变量和运行栈空间作为根。

2、标记清除方式

        标记清除(Mark and Sweep)是最早开发出的GC算法(1960年)。它的原理非常简单,首先从根开始将可能被引用的对象用递归的方式进行标记,然后将没有标记到的对象作为垃圾进行回收。

        标记清除算法有一个缺点,就是在分配了大量对象,并且其中只有一小部分存活的情况下,所消耗的时间会大大超过必要的值,这是因为在清除阶段还需要对大量死亡对象进行扫描。

3、复制收集方式

        复制收集(Copy and Collection)则试图克服标记清楚方式的这一缺点。在这种算法中,会将从根开始被引用的对象复制到另外的空间中,然后,再将复制的对象所能够引用的对象用递归的方式不断复制下去。

        和标记相比,将对象复制一份所需要的开销则比较大,因此在“存活”对象比例较高的情况下,反而会比较不利。

4、引用计数方式

        引用计数(Reference Count)方式是GC算法中最简单也最容易实现的一种,它和标记清除方式差不多是在同一时间发明出来的。

        它的基本原理是,在每个对象中保存该对象的引用计数,当引用发生增减时对计数进行更新。引用计数的增减,一般发生在变量赋值、对象内容更新、函数结束(局部变量不再被引用)等时间点。当一个对象的引用计数变为0时,则说明它将来不会再被引用,因此可以释放相应的内存空间。

        引用计数最大的缺点,就是无法释放循环引用的对象。

5、进一步改良的应用方式

        GC的基本算法,大体上都逃不出上述三种方式以及它们的衍生品。现在,通过对这三种方式进行融合,出现了一些更加高级的方式。这里,我们介绍一下其中最有代表性的三种,即分代回收、增量回收和并行回收。

(1)分代回收

        高级GC技术中最重要的一种,即分代回收(Generational GC)。

        分代回收的基本思路,是利用了一般性程序所具备的性质,即大部分对象都会在短时间内成为垃圾,而经过一定时间依然存活的对象往往拥有较长的寿命。如果寿命长的对象更容易存活下来,寿命短的对象则会被很快废弃,那么到底怎样做才能让GC变得更加高效呢?如果对分配不久,诞生时间较短的“年轻”对象进行重点扫描,应该就可以更有效地回收大部分垃圾。

(2)增量回收

        因为GC产生的中断时间与对象的数量和状态有关。因此,为了维持程序的实时性,不等到GC全部完成,而是将GC操作细分成多个部分逐一执行。这种方式被称为增量回收(Incremental GC)。

(3)并行回收

        并行回收是通过最大限度利用多CPU的处理能力来进行GC操作的一种方式。

        并行回收的基本原理是,是在原有的程序运行的同时进行GC操作,这一点和增量回收是相似的。不过,相对于在一个CPU上进行GC任务分割的增量回收来说,并行回收可以利用多CPU的性能,尽可能让这些GC任务并行(同时)进行。由于软件运行和GC操作是同时进行的,因此就会遇到和增量回收相同的问题。

二、一些语言的垃圾回收机制

        下面所有描述均未必准确严谨,仅供参考。

1、堆栈和堆

        线程是 CPU 利用率的基本单位,由程序计数器、堆栈和一组寄存器组成。

        堆栈:位于通用RAM(随机访问存储器)中,但通过堆栈指针可以从处理器那里获得直接支持。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些缓存。这是一种快速有效的分配存储方式,仅次于寄存器。创建程序时,java系统必须知道存储在堆栈内的所有项的确切生命周期,以便上下移动堆栈指针。 这一约束限制了程序的灵活性,所以虽然某些java数据存储于堆栈中---特别是对象引用,但是java对象并不存储于其中。

        堆:一种通用的内存池(也位于RAM区),用于存放所有的java对象。堆不同于堆栈的好处是:编译器不需要知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当需要一个对象时,只需要用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应代价:用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多的时间(如果确实可以在java中像在C++中一样在栈中创建对象)。

        此外,栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量。在java中,所有基本类型和引用类型都在栈中存储。栈中数据的生存空间一般在当前scopes内(就是由{...}括起来的区域)。

        堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),C中的malloc语句所产生的内存空间就在堆中。在java中,所有使用new xxx()构造出来的对象都在堆中存储,当垃圾回收器检测到某对象未被引用,则自动销毁该对象。所以,理论上说java中对象的生存空间是没有限制的,只要有引用类型指向它,则它就可以在任意地方被使用。

2、c++

        c++有两种方式管理内存,一是在堆上分配内存时手动管理。二是可以使用智能指针。c++没有使用垃圾回收,个人理解主要是为了效率的可预测性考虑。

        栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。

        堆由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表。

3、java

        堆栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,java自动管理栈和堆,程序员不能直接的设置栈或者堆。从堆和栈的功能来通俗的比较,堆主要用于存放对象,栈主要是用来执行程序的。

        java中的垃圾释放机制可能时众多编程语言内最为丰富的,不仅应用多种垃圾回收机制,而且也发展出了多种垃圾回收器。

4、python

        方法/方法调用和引用存储在堆栈内存中,所有值对象都存储在私有堆中。

        垃圾收集在 Python 中以两种方式实现:引用计数和分代。当对象的引用计数达到 0 时,引用计数垃圾回收算法立即清理该对象。如果你有一个循环,引用计数没有达到零,需等待分代垃圾收集算法运行并清理对象。

5、c#

        c#采用了分代回收、大对象堆、并发和后台回收等技术。

        垃圾回收器将堆上的内存分为了三代。刚刚分配的对象位于第0代;在第一轮回收中存活的对象在第1代,而其他所有对象为第2代。第0代和第1代对象就是所谓的短生存期(ephemeral)的代。

        垃圾回收器会将大于某一个阈值的对象(目前是85000字节)存放在一个独立的堆中,称为大对象堆(Large Object Heap, LOH)。它可以避免过量的第0代回收。

        服务器CLR会调动所有的可用核来进行垃圾回收。因此一个8核处理器的服务器可以很快完成一次完整的回收。

6、javascript

        JavaScript通过自动内存管理实现内存分配和闲置资源回收。基本思路很简单:确定哪个变量不会再使用,然后释放它占用的内存。这个过程是周期性的,即垃圾回收程序每隔一定时间(或者说在代码执行过程中某个预定的收集时间)就会自动运行。

        JavaScript运行在一个内存管理与垃圾回收都很特殊的环境。分配给浏览器的内存通常比分配给桌面软件的要少很多,分配给移动浏览器的就更少了。

        将内存占用量保持在一个较小的值可以让页面性能更好。优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。如果数据不再必要,那么把它设置为null,从而释放其引用。这也可以叫作解除引用。这个建议最适合全局变量和全局对象的属性。局部变量在超出作用域后会被自动解除引用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坐望云起

如果觉得有用,请不吝打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值