Ruby中的GC机制

ObjectSpace

在Ruby堆空间中维护一个对象池,这个对象池也被称作ObjectSpace, 你所使用的所有Ruby对象都是从这个池子中取出的,而我也会去清理池子中已经没有被使用的对象,达到循环利用的目的。

ObjectSpace对象池是由很多堆页(page)构成的,每一个页的大小为16Kb。 每页中包含408个槽(slot)。 一个槽对应一个对象,你所使用的每一个对象都在对象池中占有一个槽。这些数据可以通过下面的方式得到:

GC::INTERNAL_CONSTANTS
=> {:RVALUE_SIZE=>40, # 一个RVALUE结构体40个字节:HEAP_PAGE_OBJ_LIMIT=>408,# ...}

Ruby有这样一种类似于C语言中结构体的结构,Ruby中称其为RValue 结构,对象被存放在这个结构体中,除了对象之外,还附有一个flag,也就是在mark-and-sweep机制被mark时要使用的flag。

例如: RString (RArrayRHashRFile)

内存中,Ruby将许多这样的RValue结构体组织在一种被称为heap的数组里面。在一个进程当中,有许多这样的heap数组存在。每个RValue结构体的大小是40bytes,这也预示着这里不能存放较大的对象,故Ruby将一些较小的常用的String, Array或Hash等对象存放在此处。

例如:这是一个Ruby堆数组的概念图,包含三个字符串值以及许多其他RValue:

Ruby 1.8:简单的标记和扫描

ruby中的GC使用的是 mark and sweep机制,ruby会从根目录扫描全部对象,如果能 reach到这个对象,就使用名为FL_MARK的内部标志之一标记变量,扫描完毕没有被 mark的对象就会被回收。扫描过程是处于 Stop The word 暂停状态。

根对象是当前Ruby程序明显需要的,包括(不保证完整):

  • 全局变量(类、常量等)

  • 本地变量(Ruby当前堆栈信息)

  • C 寄存器及堆栈数据

Ruby 1.9.3:懒惰扫描

标记完成后,不一次性清除所有无用的对象,将清除的过程分散到后面的操作中,这大大降低了单次清除过程中的中断时间。

Ruby 2.0中的垃圾收集:位图标记

不是在每个RValue结构中使用FL_MARK位来指示Ruby仍在使用值并且无法释放它,而是将Ruby 2.0中的信息保存在称为“位图”的内容中(位图指映射回RValue结构的文字位集合)

1值等同于在Ruby 1.8或Ruby 1.9进程中设置的FL_MARK标志,而0等效于未设置的FL_MARK标志。换句话说,FL_MARK位已经移出RString和其他对象值结构,并进入称为位图的这个单独的存储区域,允许Unix系统的 Copy -On- Write 跨子进程共享内存。

Ruby 2.1:分代GC ,oldgen和minor标记 (一个可以逐步实现并支持C扩展的分代收集器)
Ruby2.2:引入增量标记和symbol GC (把主标记拆分为数个小任务,每个任务分散在Ruby的执行中,这样单次的暂停时间就会变得很短)
在GC module中,为我们提供了一个GC.stat方法来查看GC的状态:
2.2.8 :001 > GC.stat
{ :count                                   => 9,
  :heap_allocated_pages                    => 74,
  :heap_sorted_length                      => 75,
  :heap_allocatable_pages                  => 0,
  :heap_available_slots                    => 30168,
  :heap_live_slots                         => 29769,
  :heap_free_slots                         => 399,
  :heap_final_slots                        => 0,
  :heap_marked_slots                       => 14081,
  :heap_swept_slots                        => 8298,
  :heap_eden_pages                         => 74,
  :heap_tomb_pages                         => 0,
  :total_allocated_pages                   => 74,
  :total_freed_pages                       => 0,
  :total_allocated_objects                 => 121550,
  :total_freed_objects                     => 91781,
  :malloc_increase_bytes                   => 12768,
  :malloc_increase_bytes_limit             => 16777216,
  :minor_gc_count                          => 6,
  :major_gc_count                          => 3,
  :remembered_wb_unprotected_objects       => 182,
  :remembered_wb_unprotected_objects_limit => 362,
  :old_objects                             => 13837,
  :old_objects_limit                       => 26864,
  :oldmalloc_increase_bytes                => 13280,
  :oldmalloc_increase_bytes_limit          => 16777216 
}

GC.stat给我们返回了一个Hash,其中count所指向的值是GC运行的次数,它是由minor_gc_count + major_gc_count 来组成的。其中minor_gc_count指的是GC进行的一次小型的回收,而major_gc_count则是GC进行的是一次full垃圾回收。之所以这样区分开来,是因为每次minor回收所需要使用的时间远小于major使用的时间。每次进行GC回收的时候,会消耗很大的资源和性能。根据情况进行minor回收能够较好的实现性能的提升;heap_live_slot指的是在GC运行以来,还存活的对象的个数,heap_free_slots指的是用来存放新的对象的slot的个数。

使用GC#start /ObjectSpace.garbage_collect 来回收已作废的对象(进行的 full垃圾回收)。

Ruby在短时间内大量对象被创建的时候,是会申请较多的内存来处理的。即使在free掉相关对象后不将这部分内存归还给操作系统,因为ruby在下次分配对象的时候不需要时刻都向操作系统请求内存资源,而可以直接使用原来使用过的free_slot。

所以:

1、尽量避免创建极大的对象; 尽量复用旧对象。

2、在rails和ruby中,尽量不要把对象赋予给全局的变量或是类或module的属性,这会导致这些对象一直被保留而无法释放。

3、尽量使用最新版本的Ruby,GC的性能会更好


ruby中GC的演化: http://tmm1.net/ruby21-rgengc/

关于GC机制: http://patshaughnessy.net/2012/3/23/why-you-should-be-excited-about-garbage-collection-in-ruby-2-0

关于ruby中栈堆:http://patshaughnessy.net/2012/1/18/seeing-double-how-ruby-shares-string-values

转载于:https://my.oschina.net/u/3970558/blog/2196400

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值