Skip to content

Latest commit

 

History

History
1734 lines (1391 loc) · 72.3 KB

chapter05.textile

File metadata and controls

1734 lines (1391 loc) · 72.3 KB
layout title
default
Garbage Collection

Translated by Sebastian Krause

Chapter 5: Garbage Collection

A conception of an executing program

It’s all of a sudden but at the beginning of this chapter, we’ll
learn about the memory space of an executing program. In this chapter
we deal with the lower level parts of a computer quite a bit, so without
preliminary knowledge it’ll be hard to follow. And it’ll be also necessary
for the following chapters. If we finish this, the rest will be a piece of cake.

Memory Segments

The memory space of a general C program has the following parts:

  1. the text area
  2. a place for static and global variables
  3. the machine stack
  4. the heap

The text area is where the code lies. Obviously the second area holds static and global variables.
Function calls, along with their arguments and local variables are piled up in the machine stack.
The heap is allocated by `malloc()`.

Let’s talk a bit about number three, the machine stack.
Obviously, because it’s the machine-“stack” it has the structure of a stack.
In other words new stuff is piled on top of it.
The new in a word fast one is piled up. There is a large a little more unit when logically seeing when the value is actually piled up in the machine stack because it piles up by a detailed unit of `int` 1 piece etc. It is called stack frame (stack frame).

One stack frame corresponds to one function call. Or in other words when there is a function call, one stack frame is added.
With the return the stack frame descends by one. Really simplified the stack frame looks as shown below.

Machine stack
䞊先端above (top)
䞋末端below (bottom)
スタックフレヌム stack frame
珟圚実行䞭の関数フレヌム presently executing function frame

In this picture the top of the stack is depicted above,
but this it is not necessarily always the case that it goes
from low addresses to high addresses. For instance on the x86
machine the stack goes from high to low addresses.

`alloca()`

By using `malloc()` it’s possible to get an abitrarily large memory
area of the heap. `alloca()` is the machine stack version of it.
But unlike `malloc()` it’s not necessary to free the memory allocated
with `alloca()`. Or one should say:
it is freed automatically with the return of the function.
That’s why it’s not possible to have an allocated value as the return
value (?). It’s the same as “You must not return the pointer to
a local variable.”

ここたではいい。長さを実行時に倉えられる配列をロヌカルに割り圓おられる、
ずいう皋床のこずだ。

However there exist environments where there is no native `alloca()`.
In this case there are still many who would like to use alloca(),
they write versions of this function in C.

だが䞖の䞭にはネむティブの`alloca()`がない環境ずいうのがある。そういう堎
合でも`alloca()`は䜿いたいず思う人は倚いので、同じ働きをする関数がCで
曞いおあったりする。ただこの堎合は「解攟する必芁がない」ずいう特城だけ
が実装されおいお、マシンスタック䞊にメモリを割り圓おおくれるずは限らな
い。ず蚀うか、普通は取らない。それができるならそもそもネむティブの
`alloca()`が実装できるずいうこずだからだ。If you could do this a native
alloca() could have been implemented in the first place.

How can one implement alloca() in C? The simplest implementation is:
first allocate memory normally with malloc(). Then remember the function
which called alloca() and the assigned addresses in a global list.
After that whenever alloca() is called check this list if the calling
function has already finished. If so set the allocated memory free with
free().

Cで実装した`alloca()`の動き
D→alloca(8)の察応を蚘録 record the relation D→ alloca(8)
B→alloca(32)の察応を蚘録 record the relation B→ alloca(32)
Dで割り圓おたメモりを解攟 release the memory allocated in D

The missing/alloca.c in ruby is an example of an emulated alloca().

Overview

From here on we can talk at last about the main subject of this chapter:
garbage collection.

What is GC?

Objects are normally on top of the memory. Naturally, if a lot of objects are created, a lot of memory is used. If memory
were infinite there would be no problem but at present there is always a limit to memory. That’s why the memory which is not
used anymore must be collected and recycled. More concretely the memory received through malloc() must be returned with
free().

However, it is difficult to leave the management of malloc() and free() to the
program. Especially in object oriented programming if objects reference each other,
it is difficult to tell when to release memory.

そこでガヌベヌゞコレクションだ。
There garbage collection comes in.
With garbage collection the memory which has become unnecessary,
is gathered and automatically freed. With garbage collection
the worry, when to release some memory with @free(), has become
unnecessary. Depending on this the writing of a program becomes
considerably easier.

Besides, in some book there was written: Garbage collection cleans up
usable memory which has become fragmented. This is an action called
compaction. It makes the memory more compact.
ずころで、以前䜕かの本に「䜿えるメモリがこたぎれになっおいるのを敎理す
るのがGC」ず曞いおあったのを芋たこずがある。これは「
コンパクション(compaction)」ず蚀う䜜業だ。
コンパクトになるからコンパクションである。
コンパクションをやるずメモリキャッシュにヒットしやすくなるのでスピヌド
アップにそれなりの効果があるのだが、これはGCの䞻目的ではない。GCの目的
はあくたでメモリの回収である。実際、メモリの回収はしおもコンパクション
はしないGCも倚い。`ruby`のGCもコンパクションはしない。

では具䜓的にどんなシステムでGCが䜿えるのだろうか。
CやC++ではアドオンずしお䜿える
Boehm GC\footnote{Boehm GC `http://www.hpl.hp.com/personal/Hans_Boehm/gc`}ず
いうものがある。
たたJavaやPerl、Python、C#、Eiffelなど最近の蚀語にずっおはGCは
暙準装備だ。そしおもちろんRubyにもGCがある。
本章では`ruby`のGCの詳现を远っおいこう。察象ずなるファむルは`gc.c`である。

What does GC do?

Before we explain the GC algorithm, we should explain what garbage collection
is. In other words how can we tell that memory has become unnecessary?

To keep it more concrete let’s simplify the structure and assume that there
are only objects and links. This would look as shown in figure 3.

Objects

Object referred in global variables or objects on top of the language stack
are surely necessary. And objects referred to in instance variables of these
objects are also necessary. Again following links we reach more necessary
objects.

To put it more systematically, the necessary objects are all objects which
can be reached recursively via links starting at the surely necessary objects.
This is depicted in figure 4. Left of the line are all obviously necessary objects.
The objects which can be reached from them are colored black. These are the
necessary objects. The rest of the objects can be released.

Necessary and unnecessary objects
ルヌトオブゞェクト root object
䞍芁なオブゞェクト unused object
ルヌトオブゞェクトから参照されおいるオブゞェクト an object referenced by the root object

To put it into jargon , the surely necessary objects
are the garbage collection root. It becomes the root
of the tree of object links.

Mark and Sweep

GC was first implemented in Lisp. The GC in Lisp, the world’s first
GC was of the type mark&sweep. Ruby’s GC is of the same type.

Garbage collection with Mark&sweep is pretty close to our definition of
“necessary object”. First a mark is put on the root objects. From this starting
point all elements which are reached will also be marked. This is the mark phase.

At some point no new elements can be reached. The yielded elements are all checked
and the objects which do not have a marked are all released. This is the sweep.

There are two advantages.

  • There does not need to be any (or almost any) concern for garbage collection
    outside the implementation of GC.
  • Cycles can also be released. (Look up cycles in the section “Reference Count” below)

There are also two disadvantages.

  • In order to make the sweep every object must be touched at least once.
  • The load of the GC is concentrated at one point.

If you use the emacs editor there appears sometimes a Garbage collecting...
and it completely stops reacting. That is an example of the second disadvantage.
But this point can be alleviated by modifying the algorithm (incremental GC).

Stop and Copy

Stop and Copy is a variation of Mark and Sweep. First we prepare the object area in several
ways. We simplify by assuming there are two areas A and B. We mark both as active.
Always when creating an object we mark it as active.

Stop and Copy (1)

The garbage collection starts as with mark&sweep at the root elemnts.
But instead of marking elements it moves them to another region (Fig.6).
When all the links have been followed the elements which remain in A
are all discarded. Next we make B active.

Stop and Copy (2)

Stop and Copy has two advantages:

  • Compaction happens at the same time as collecting the memory
  • Objects that reference each other move closer together, improving memory cache hits

The second point is both good and bad:

  • The area needed for storing objects needs to be twice as big
  • The position of the object changes

There does not seem to be an allaround positive thing.

Reference counting

Reference count differs a bit from the aforementioned, the check(??)
code is distributed in several places.

First each elements get an integer counter. When it is referenced via
variables or arrays the counter of the referenced object is increased.
When the reference ends the counter is decreased. When the counter
becomes zero the object is released. This is the reference counter method.
(Fig.7)

Reference counting

There are two advantages:

  • The load of GC is distributed to the entire program.
  • The object that became unnecessary is immediately freed.

And there are two disadvantages.

  • Handling the counters tends to be forgotten.
  • When doing it naively cycles are not released.

Let’s explain about the second point. A cycle is
a cycle of references as shown in figure 8.
If this is the case the counters will never vanish
and the objects will not be released.

Cycle

By the way, latest Python(2.2) uses reference count GC and cycles can be freed.
However, it is not because of the reference count, but because it occasionally uses mark and sweep to check for them.

Object Management

Ruby’s garbage collection is only concerned with ruby objects.
Objects must be created and managed by ruby. In other words
if the user allocates memory by himself ruby want take care of it.
For instance the following function will create a memory leak in ruby.

void not_ok()
{
    malloc(1024);  /* receive memory and discard it */
}

However, the following function does not cause the memory leak.

void this_is_ok()
{
    rb_ary_new();  /* create a ruby array and discard it */
}

rb_ary_new() uses Ruby’s proper interface to allocate memory.
Ruby’s garbage collection will take care of it.

`struct RVALUE`

As objects are really structures, object management is really
management of these structures. Of course there are also the non-pointer
objects like Fixnum Symbol nil true false which are not of this form.
We will not deal with this distinction here.

The size of the structure varies for the different types, how can we
keep management simple? We declare a union type of all the structures of
embedded classes. When we deal with memory we will deal with this union type.
The declaration of this union type is as follows.

▌ `RVALUE`


211 typedef struct RVALUE {
212 union {
213 struct {
214 unsigned long flags; /* 0 if not used */
215 struct RVALUE *next;
216 } free;
217 struct RBasic basic;
218 struct RObject object;
219 struct RClass klass;
220 struct RFloat flonum;
221 struct RString string;
222 struct RArray array;
223 struct RRegexp regexp;
224 struct RHash hash;
225 struct RData data;
226 struct RStruct rstruct;
227 struct RBignum bignum;
228 struct RFile file;
229 struct RNode node;
230 struct RMatch match;
231 struct RVarmap varmap;
232 struct SCOPE scope;
233 } as;
234 } RVALUE;

(gc.c)

The element of struct RVALUE is only one struct.
`struct RVALUE`は芁玠が䞀぀だけの構造䜓だ。`union`を盎接䜿わないのはデバッ
グや将来の拡匵のずきに簡単にメンバを増やせるようにするためだそうである。

たずは共甚䜓の最初の芁玠`free.flags`に泚目しよう。コメントには「䜿われお
いないずきはれロ」ず曞いおあるが本圓だろうか。ただ䜿っおいるオブゞェク
トの`free.flags`が偶然0になるこずはないのだろうか。

第2章『オブゞェクト』で芋たように、党おのオブゞェクト構造䜓は
`struct RBasic`を最初の芁玠に持぀。だから共甚䜓のどの芁玠からアクセスしおも
`obj→as.free.flags`は`obj→as.basic.flags`ず曞くのず同じこずだ。そしお
オブゞェクトは必ずフラグに構造䜓型フラグ(`T_STRING`など)を持ち、しか
もそのフラグは党お0以倖なので、「生きおいる」オブゞェクトのフラグが偶
然0になるこずはない。぀たりフラグを0にするこずで「死に」オブゞェクトを
衚すのは必芁十分だず確かめられる。

Object heap

The memory for all the object structures has been brought together in global variable `heaps`. Hereafter, let’s call this an object heap.。

▌ Object heap


239 #define HEAPS_INCREMENT 10
240 static RVALUE **heaps;
241 static int heaps_length = 0;
242 static int heaps_used = 0;
243
244 #define HEAP_MIN_SLOTS 10000
245 static int *heaps_limits;
246 static int heap_slots = HEAP_MIN_SLOTS;

(gc.c)

heaps is an array of arrays of struct RVALUE. The contained arrays
are each a heap. The elements of heap are each a slot.
(Fig.9)。

Heap slots

The length of heaps can be changed in heap_length. The number of
the slots actually in use is heaps_used. The length of each heap
is in the corresponding heaps_limits[index]. In other words the
structure of the object heap is as depicted in figure 10.

conceptual diagram of `heaps` in memory

This structure must necessarily be this way.
この構造には必然性がある。䟋えば党おの構造䜓を䞀぀の配列に配眮するず
メモリ空間は最もコンパクトになるが、アドレスが倉わっおしたう恐れがある
ので`realloc()`できない。`VALUE`は単なるポむンタだからだ。

ずあるJavaの実装だず`VALUE`に盞圓するものがアドレスではなくおオブゞェク
トのむンデックスで、ポむンタテヌブルを経由しお取り扱われるようになっお
いるため、オブゞェクトを移動するこずができる。ただしその堎合は
オブゞェクトアクセスのたびに配列のむンデクシングが入るので倚少
パフォヌマンスは萜ちる。

䞀方`RVALUE`ぞのポむンタ(぀たり`VALUE`)の䞀次元配列にした堎合はどうだろ
うか。これは䞀芋うたくいきそうだが、GCのずきに困っおしたう。ずいうのも、
これから詳しく曞くずおり、`ruby`のGCでは「`VALUE`(`RVALUE`ぞのポむンタ)ら
しい」敎数を知る必芁があるからだ。党おの`RVALUE`がおんでバラバラのアドレ
スに配眮されおいるず、党おの`RVALUE`のアドレスず「ポむンタかもしれない」
敎数党おをそれぞれ比范しなければいけない。これではGCの速床は O(n^2) 以
䞊のオヌダヌになり、容認できない。

以䞊の条件から、オブゞェクトヒヌプはある皋床アドレスにたずたりがあり、
しかも䜍眮ず総量は制限されないような構造にするのがよいずいうわけだ。

`freelist`

䜿われおいない`RVALUE`は`freelist`から始たるリンク
リストに䞀本に぀ながれお管理される。`RVALUE`の`as.free.next`はそのため
に䜿うリンクだ。

▌ `freelist`


236 static RVALUE *freelist = 0;

(gc.c)

`add_heap()`

デヌタ構造がわかったずころでヒヌプを远加する関数`add_heap()`を読んでみよ
う。この関数はやたらず本筋以倖の蚘述がうるさいので、゚ラヌ凊理やキャス
トを省いお簡単にしたものを芋せる。

â–Œ `add_heap()`(簡玄版)


static void
add_heap()
{
RVALUE *p, *pend;

/* 必芁ならheapsをのばす */ if (heaps_used == heaps_length) { heaps_length += HEAPS_INCREMENT; heaps = realloc(heaps, heaps_length * sizeof(RVALUE*)); heaps_limits = realloc(heaps_limits, heaps_length * sizeof(int)); } /* heapを䞀぀増やす */ p = heaps[heaps_used] = malloc(sizeof(RVALUE) * heap_slots); heaps_limits[heaps_used] = heap_slots; pend = p + heap_slots; if (lomem == 0 || lomem > p) lomem = p; if (himem < pend) himem = pend; heaps_used++; heap_slots *= 1.8; /* 割り圓おたRVALUEをfreelistに぀なぐ */ while (p < pend) { p→as.free.flags = 0; p→as.free.next = freelist; freelist = p; p++; }

}

以䞋の点を確認しおおいおほしい。

  • `heap`の長さは`heap_slots`
  • その`heap_slots`は`heap`が䞀぀増えるごずに1.8倍になっおいく
  • `heaps[i]`の長さ(ヒヌプ生成時の`heap_slots`の倀)は`heaps_limits[i]`に栌玍されおいる

たた`lomem`ず`himem`を倉曎しおいるのもこの関数だけなので、この関数だけから
仕組みが理解できる。この倉数にはオブゞェクトヒヌプの最䞋䜍アドレスず
最䞊䜍アドレスが入っおいるのである。この倀はあずで「`VALUE`っぜい」敎数を
刀断するずきに䜿う。

`rb_newobj()`

以䞊の点を総合しお考えればオブゞェクトを生成する方法はすぐにわかる。
`freelist`に぀ながれおいる`RVALUE`があればそれを䜿い、なければGCするか、
ヒヌプを増やせばよい。オブゞェクト生成を行う関数`rb_newobj()`を読んで
確かめおみよう。

▌ `rb_newobj()`


297 VALUE
298 rb_newobj()
299 {
300 VALUE obj;
301
302 if (!freelist) rb_gc();
303
304 obj = (VALUE)freelist;
305 freelist = freelist→as.free.next;
306 MEMZEROobj, RVALUE, 1);
307 return obj;
308 }

(gc.c)

`freelist`が0、぀たり䜙りの構造䜓がないならGCを起動しお領域を䜜る。
もし䞀぀もオブゞェクトを回収できなくおも、`rb_gc()`の䞭で新しい
領域を割り圓おおくれるので問題ない。そしお`freelist`から構造䜓を
䞀぀取り出し、`MEMZERO()`で0を充填、それを返す、ずなる。

Mark

説明したずおり、`ruby`のGCはマヌク&スむヌプである。マヌクずは具䜓的には
`FL_MARK`フラグをセットするこずだ。䜿われおいる`VALUE`を探しおは
`FL_MARK`をセットし、これで党郚調べたず思ったらオブゞェクトヒヌプを芋お、
`FL_MARK`がセットされおいないオブゞェクトを解攟すればいい。

`rb_gc_mark()`

`rb_gc_mark()`はオブゞェクトを再垰的にマヌクする関数である。

▌ `rb_gc_mark()`


573 void
574 rb_gc_mark(ptr)
575 VALUE ptr;
576 {
577 int ret;
578 register RVALUE obj = RANY;
579
580 if (rb_special_const_p(ptr)) return; /
special const not marked /
581 if (obj→as.basic.flags == 0) return; /
free cell /
582 if (obj→as.basic.flags & FL_MARK) return; /
already marked */
583
584 obj→as.basic.flags |= FL_MARK;
585
586 CHECK_STACK(ret);
587 if (ret) {
588 if (!mark_stack_overflow) {
589 if (mark_stack_ptr – mark_stack < MARK_STACK_MAX) {
590 *mark_stack_ptr = ptr;
591 mark_stack_ptr++;
592 }
593 else {
594 mark_stack_overflow = 1;
595 }
596 }
597 }
598 else {
599 rb_gc_mark_children(ptr);
600 }
601 }

(gc.c)

The definition of RANY() is as follows. It is not particularly important.

▌ `RANY ((RVALUE*)(o))

(gc.c)

最初にポむンタでないものや既に解攟したオブゞェクトのチェック、
マヌクされたオブゞェクトの再垰チェックがあり、

obj->as.basic.flags |= FL_MARK;

で`obj`(぀たり関数の匕数`ptr`)がマヌクされる。
そうしたら次は`obj`から出おいる参照を蟿っおマヌクする番である。
`rb_gc_mark_children()`がそれだ。

その他の、`CHECK_STACK()`から始たっおいろいろず曞いおあるのはマシンスタッ
ク溢れを防ぐための仕掛けである。`rb_gc_mark()`は再垰呌び出しを䜿っおオブ
ゞェクトをマヌクするため、倧きなオブゞェクトクラスタがあるずマシンスタッ
クの長さが足りなくなるこずがある。そこでスタックが溢れそうだったら再垰
を䞭止しおオブゞェクトをグロヌバルなリストに積んでおき、あずからもう䞀
床マヌクしなおすようにしおいるのだ。
このコヌドは本筋ではないので省略する。

`rb_gc_mark_children()`

さお、`rb_gc_mark_children()`だが、
内郚の型をひたすら䞊べお地道にマヌクしおいるだけなので長いうえに
面癜くない。ここでは単玔な列挙郚分は省略しお茉せる。

▌ `rb_gc_mark_children()`


603 void
604 rb_gc_mark_children(ptr)
605 VALUE ptr;
606 {
607 register RVALUE obj = RANY;
608
609 if (FL_TEST(obj, FL_EXIVAR)) {
610 rb_mark_generic_ivar((VALUE)obj);
611 }
612
613 switch (obj→as.basic.flags & T_MASK) {
614 case T_NIL:
615 case T_FIXNUM:
616 rb_bug(“rb_gc_mark() called for broken object”);
617 break;
618
619 case T_NODE:
620 mark_source_filename(obj→as.node.nd_file);
621 switch (nd_type(obj)) {
622 case NODE_IF: /
1,2,3 /
623 case NODE_FOR:
624 case NODE_ITER:
/
    省略     /
749 }
750 return; /
basic.klassはマヌクしなくおよい /
751 }
752
753 rb_gc_mark(obj→as.basic.klass);
754 switch (obj→as.basic.flags & T_MASK) {
755 case T_ICLASS:
756 case T_CLASS:
757 case T_MODULE:
758 rb_gc_mark(obj→as.klass.super);
759 rb_mark_tbl(obj→as.klass.m_tbl);
760 rb_mark_tbl(obj→as.klass.iv_tbl);
761 break;
762
763 case T_ARRAY:
764 if (FL_TEST(obj, ELTS_SHARED)) {
765 rb_gc_mark(obj→as.array.aux.shared);
766 }
767 else {
768 long i, len = obj→as.array.len;
769 VALUE *ptr = obj→as.array.ptr;
770
771 for (i=0; i < len; i++) {
772 rb_gc_mark(
ptr++);
773 }
774 }
775 break;

/*     省略     */ 837 default: 838 rb_bug(“rb_gc_mark(): unknown data type 0x%x(0x%x) %s”, 839 obj→as.basic.flags & T_MASK, obj, 840 is_pointer_to_heap(obj) ? “corrupted object” : “non object”); 841 } 842 }

(gc.c)

`rb_gc_mark()`を再垰呌び出ししおいるな、ずいうこずだけ確認しおもらえば
それでよい。省略した郚分には`NODE`ず`T_xxxx`がそれぞれ列挙されおいる。
`NODE`のこずは第二郚で玹介する。

それず`T_DATA`(拡匵ラむブラリに䜿う構造䜓)のマヌクの郚分だけは確認した
いこずがあるので芋おおこう。このコヌドは二぀めの`switch`文から抜粋した。

▌ `rb_gc_mark_children()`-`T_DATA`


789 case T_DATA:
790 if (obj→as.data.dmark) (*obj→as.data.dmark)(DATA_PTR(obj));
791 break;

(gc.c)

ここは`rb_gc_mark()`やその類䌌の関数ではなく、ナヌザからもらった関数
`dmark`を䜿っおいる。その䞭ではもちろん`rb_gc_mark()`なりなんなりを䜿
うだろうが、䜿わないこずもありうる。䟋えば極端な堎合で蚀うず、ナヌザ
定矩のオブゞェクトに`VALUE`が入っおいないならマヌクする必芁はないだろう。

`rb_gc()`

ここたででオブゞェクト単䜍の話は枈んだので、ここからは党䜓を統蜄する関数
`rb_gc()`を芋おいくこずにする。ここでマヌクしおいるのが「必芁だずいうこずが
明らかに分かっおいるオブゞェクト」぀たり「GCのルヌト」だ。

▌ `rb_gc()`


1110 void
1111 rb_gc()
1112 {
1113 struct gc_list list;
1114 struct FRAME * volatile frame; /
gcc 2.7.2.3 -O2 bug?? */
1115 jmp_buf save_regs_gc_mark;
1116 SET_STACK_END;
1117
1118 if (dont_gc || during_gc) {
1119 if (!freelist) {
1120 add_heap();
1121 }
1122 return;
1123 }

/*   党おのルヌトをマヌクする   */

1183 gc_sweep();
1184 }

(gc.c)

マヌクすべきルヌトはこの埌で順番に芋せおいくが、䞀点だけ觊れおおこう。

`ruby`ではCPUのレゞスタやマシンスタックもルヌトずする。
それは぀たりCのロヌカル倉数や匕数も勝手にマヌクされるずいうこずだ。
䟋えば

static int
f(void)
{
    VALUE arr = rb_ary_new();

    /*   いろいろする   */
}

ずいうように、ただ倉数に入れおおくだけでそのオブゞェクトは保護されるの
だ。これは`ruby`のGCの非垞に倧きな特城である。この機胜があるからこそ
`ruby`の拡匵ラむブラリは異様に曞き易くなっおいるのだ。

ただしもちろんスタックに眮かれおいるのは`VALUE`ばかりではない。䜕の関係も
ない倀もたくさん存圚する。そのあたりをどうやっお解決しおいるのかがGCの
実装を芋おいくずきの鍵だ。

Ruby Stack

最初はむンタプリタが䜿っおいる(`ruby`の)スタックフレヌムを
マヌクする。第䞉郚たで行けばこれが䜕者かはわかるので今はあたり
深く考えなくおもいい。

▌ Marking the Ruby Stack


1130 /* mark frame stack */
1131 for (frame = ruby_frame; frame; frame = frame→prev) {
1132 rb_gc_mark_frame(frame);
1133 if (frame→tmp) {
1134 struct FRAME *tmp = frame→tmp;
1135 while (tmp) {
1136 rb_gc_mark_frame(tmp);
1137 tmp = tmp→prev;
1138 }
1139 }
1140 }
1141 rb_gc_mark((VALUE)ruby_class);
1142 rb_gc_mark((VALUE)ruby_scope);
1143 rb_gc_mark((VALUE)ruby_dyna_vars);

(gc.c)

The frame, the class scope, the local variable scope, and the block local variable at that time are maintained respectively by the variable to which `ruby_frame ruby_class ruby_scope ruby_dyna_vars` points at the head of the stack of the evaluation machine respectively.

Register

次にCPUのレゞスタをマヌクする。

▌ marking the registers


1148 FLUSH_REGISTER_WINDOWS;
1149 /* ここで党おのレゞスタがjmp_bufに保存されなければならない /
1150 setjmp(save_regs_gc_mark);
1151 mark_locations_array((VALUE
)save_regs_gc_mark,
sizeof(save_regs_gc_mark) / sizeof(VALUE *));

(gc.c)

`FLUSH_REGISTER_WINDOWS` is special. We will come to it later.

`setjmp()`は本圓は遠隔ゞャンプのための関数なのだけど、
その副䜜甚ずしお匕数(`jmp_buf`型の倉数)にレゞスタの内容を
保存するようになっおいる。
それを利甚しおレゞスタの䞭身をマヌクしようずいうわけだ。
このあたりはかなり裏技っぜい。

ただしdjgppずHuman68kだけは特別扱いされおいる。
djgppずいうのはDOS甚の`gcc`環境。
Human68kはシャヌプのX680x0シリヌズのOSだ。
この二぀の環境では通垞の`setjmp()`では党おのレゞスタが曞きこたれないよ
うで、`setjmp()`を以䞋のようにむンラむンアセンブラで再定矩しお明瀺的にレゞスタを曞き出し
おいる。

â–Œ オリゞナル版`setjmp`


1072 #ifdef GNUC
1073 #if defined(human68k) || defined(DJGPP)
1074 #if defined(human68k)
1075 typedef unsigned long rb_jmp_buf8;
1076 asm (“.even\n\ 2バむトアラむンメント
1077 rb_setjmp:\n\ 関数rbsetjmp()のラベル
1078 move.l 4(sp),a0\n\ 第䞀匕数をレゞスタa0にロヌド
1079 movem.l d3-d7/a3-a5,(a0)\n\ a0の指す先にレゞスタをコピヌ
1080 moveq.l #0,d0\n\ d0に0をセット(返り倀)
1081 rts”); return
1082 #ifdef setjmp
1083 #undef setjmp
1084 #endif
1085 #else
1086 #if defined(DJGPP)
1087 typedef unsigned long rb_jmp_buf6;
1088 asm (“.align 4\n\ 4バむトアラむンメントを指瀺
1089 rb_setjmp:\n\ 関数rbsetjmp()のラベル
1090 pushl %ebp\n\ ebpをスタックにプッシュ
1091 movl %esp,%ebp\n\ スタックポむンタをebpにセット
1092 movl 8(%ebp),%ebp\n\ 第䞀匕数を拟いebpにセット
1093 movl %eax,(%ebp)\n\ 以䞋、各レゞスタを
1094 movl %ebx,4(%ebp)\n\ ebpの指す先にストア
1095 movl %ecx,8(%ebp)\n\
1096 movl %edx,12(%ebp)\n\
1097 movl %esi,16(%ebp)\n\
1098 movl %edi,20(%ebp)\n\
1099 popl %ebp\n\ ebpをスタックから埩垰
1100 xorl %eax,%eax\n\ eaxに0をセット(返り倀)
1101 ret”); return
1102 #endif
1103 #endif
1104 int rb_setjmp (rb_jmp_buf);
1105 #define jmp_buf rb_jmp_buf
1106 #define setjmp rb_setjmp
1107 #endif /* human68k or DJGPP /
1108 #endif /
GNUC */

(gc.c)

アラむンメント(alignment)ずいうのはメモリに倉数を眮くずきの
制玄のこずだ。
䟋えば32ビットマシンでは普通`int`は32ビットだが、メモリのど
こからでも32ビット取り出せるわけでは必ずしもない。特にRISCマシンだず制
玄が匷く、「4の倍数バむトから」ずか「偶数バむトから」ずいうふうに決め
られおいる。そういう制玄があるほうがメモリアクセスナニットを単玔化でき
る(その結果高速化できる)からだ。「4の倍数バむトから」ずいう制玄が
あるなら「4バむトアラむンメント」ず呌ぶ。

たたdjgppやHuman68kの`cc`ではコンパむラが関数名の先頭にアンダヌラむンを
付ける芏玄があるようだ。だからアセンブラでCの関数を曞くずきは自分で先
頭にアンダヌラむン(`_`)を付けなければならない。このタむプの芏玄はラむ
ブラリ関数ず名前が重耇しないようにするための工倫だ。UNIXでも少し前たでは
アンダヌラむンを付けおいたそうだが今はほがなくなっおいる。

さおここたででレゞスタの䞭身を`jmp_buf`に曞きだせたので、
次のコヌドでマヌクする。

â–Œ レゞスタのマヌク(再掲)


1151 mark_locations_array((VALUE*)save_regs_gc_mark,
sizeof(save_regs_gc_mark) / sizeof(VALUE *));

(gc.c)

`mark_locations_array()`ずいうのが初めお出おきた。
これは別項目ずしお芋おおこう。

`mark_locations_array()`

▌ `mark_locations_array()`


500 static void
501 mark_locations_array(x, n)
502 register VALUE x;
503 register long n;
504 {
505 while (n—) {
506 if (is_pointer_to_heap((void *)
x)) {
507 rb_gc_mark(*x);
508 }
509 x++;
510 }
511 }

(gc.c)

この関数は配列をたずめおマヌクするための関数なのだが、これたでのマヌク
関数ずは少し違う。これたでは確実に`VALUE`がある(オブゞェクトを指すポむ
ンタだ)ずわかっおいる堎所をマヌクしおきた。しかし今床マヌクしようずし
おいるのはレゞスタ領域で、ここには`VALUE`以倖のものがあるこずも十分考え
られる。そこで、たずその数倀が`VALUE`であるか(ポむンタであるか)どうか
調べおみお、それらしく芋えるならば党おポむンタずしお扱うこずにする。
このような手法を「保守的GC(conservative GC)」ず呌ぶ。
「ずりあえず安党偎に倒す」ずいうずころが保守的だずいうこずらしい。

では次はその「`VALUE`っぜいかどうか」を調べる関数
`is_pointer_to_heap()`を芋よう。

`is_pointer_to_heap()`

▌ `is_pointer_to_heap()`


480 static inline int
481 is_pointer_to_heap(ptr)
482 void ptr;
483 {
484 register RVALUE *p = RANY;
485 register RVALUE *heap_org;
486 register long i;
487
488 if (p < lomem || p > himem) return Qfalse;
489
490 /
pがポむンタである可胜性があるか調べる /
491 for (i=0; i < heaps_used; i++) {
492 heap_org = heaps[i];
493 if (heap_org <= p && p < heap_org + heaps_limits[i] &&
494 ((((char
)p)-((char*)heap_org))%sizeof(RVALUE)) == 0)
495 return Qtrue;
496 }
497 return Qfalse;
498 }

(gc.c)

簡単に説明するず次のようになる。

  1. `RVALUE`があるアドレスの最䞋端ず最䞊端の間にあるか調べる
  2. 各ヒヌプの範囲内にあるかどうか調べる
  3. その数倀が`RVALUE`の先頭を指しおいるかどうか確かめる

このような仕組みなので、間違っお本圓は`VALUE`でない倀を`VALUE`ず
扱っおしたうこずも圓然ある。しかし少なくずも䜿っおいる
`VALUE`を芋付けられないこずはない。
それもこれだけのテストをしおいれば意図的でない
限りそうそう`VALUE`でない倀を拟うこずはないず思われるので、GCで
埗られる利点を考えれば十分に劥協できる。ずいうわけだ。

The Register Window

最埌に埌回しにしおおいた`FLUSH_REGISTER_WINDOWS()`に぀いお。

レゞスタりィンドり(register windows)ずはマシンスタックの䞀郚をCPUの
䞭に眮いおおくこずができる機構である。ようするに甚途を絞ったキャッシュ
だ。最近はSparcアヌキテクチャにしか存圚しない。レゞスタりィンドりにも
`VALUE`が入っおいる可胜性があるので、これも前もっおメモリに萜ずしおおく
必芁がある。

マクロの䞭身はこんな感じだ。

▌ `FLUSH_REGISTER_WINDOWS`


125 #if defined(sparc) || defined(sparc)
126 # if defined(linux) || defined(linux)
127 #define FLUSH_REGISTER_WINDOWS asm(“ta 0×83”)
128 # else /* Solaris, not sparc linux /
129 #define FLUSH_REGISTER_WINDOWS asm(“ta 0×03”)
130 # endif
131 #else /
Not a sparc */
132 #define FLUSH_REGISTER_WINDOWS
133 #endif

(defines.h)

`asm(
)`は埋め蟌み
アセンブラだ。ただしアセンブラずは蚀っおもこの`ta`ずいう呜什は
特暩呜什の
コヌル、぀たりCPUでなくOSの呌び出しである。だからこそOSごずに呜什が
違うのだ。なお、コメントにはLinuxずSolarisしか曞いおいないが実際には
FreeBSDやNetBSDもSparcで動くのでこのコメントは間違いである。

それず、Sparcでない堎合はそもそもフラッシュする必芁がないので、
`FLUSH_REGISTER_WINDOWS`を無ず定矩しおいる。このような、
マクロを無に還す手法はデバッグ出力などにも䜿える超有名手法だ。

マシンスタック

ではたた`rb_gc()`の続きに戻ろう。今床はマシンスタックに眮かれた
`VALUE`をマヌクする。

â–Œ マシンスタックのマヌク


1152 rb_gc_mark_locations(rb_gc_stack_start, (VALUE*)STACK_END);
1153 #if defined(human68k)
1154 rb_gc_mark_locations((VALUE*)((char*)rb_gc_stack_start + 2),
1155 (VALUE*)((char*)STACK_END + 2));
1156 #endif

(gc.c)

`rb_gc_stack_start`がスタックの開始アドレス(スタックの末尟)で
`STACK_END`が終了アドレス(先端)らしい。
そしお`rb_gc_mark_locations()`が実際にスタック領域をマヌクする。

`rb_gc_mark_locations()`が二回あるのはスタックが4バむトアラむンメントで
ないアヌキテクチャぞの察策である。`rb_gc_mark_locations()`は
`sizeof(VALUE)`単䜍でマヌクを詊すので、2バむトアラむンメントの環境だずう
たくマヌクできないこずがある。そこで2バむトズラしおもう䞀床マヌクする
わけだ。

では`rb_gc_stack_start`、`STACK_END`、`rb_gc_mark_locations()`の
䞉぀を順番に芋おいこう。

`Init_stack()`

最初は`rb_gc_stack_start`だ。この倉数は`Init_stack()`䞭でだけセットさ
れる。`Init_`ずいう名前から想像が぀くかもしれないが、この関数は`ruby`むン
タプリタの初期化の時点で呌ばれる。

▌ `Init_stack()`


1193 void
1194 Init_stack(addr)
1195 VALUE addr;
1196 {
1197 #if defined(human68k)
1198 extern void *_SEND;
1199 rb_gc_stack_start = SEND;
1200 #else
1201 VALUE start;
1202
1203 if (!addr) addr = &start;
1204 rb_gc_stack_start = addr;
1205 #endif
1206 #ifdef HAVE_GETRLIMIT
1207 {
1208 struct rlimit rlim;
1209
1210 if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
1211 double space = (double)rlim.rlim
cur
0.2;
1212
1213 if (space > 1024*1024) space = 1024*1024;
1214 STACK_LEVEL_MAX = (rlim.rlim_cur – space) / sizeof(VALUE);
1215 }
1216 }
1217 #endif
1218 }

(gc.c)

重芁なのは真ん䞭の郚分だけだ。぀たり適圓にロヌカル倉数(スタックに確保される)を定矩しおそのア
ドレスを`rb_gc_stack_start`ずする。`human68k`のコヌドにある
`_SEND`ずいうのはコンパむラのラむブラリかシステムが定矩した倉数だろう。
圓然`Stack END`の略であろうず想像できる。

䞀方そのあずの`HAVE_GETRLIMIT`でくくっおあるコヌドではスタックの長さを
調べおゎニョゎニョずやっおいるようだ。これも`rb_gc_mark_children()`での
スタック溢れ防止の䞀貫である。無芖しおいい。

`STACK_END`

次にスタックの先端を怜出するマクロ`STACK_END`を芋る。

▌ `STACK_END`


345 #ifdef C_ALLOCA
346 # define SET_STACK_END VALUE stack_end; alloca(0);
347 # define STACK_END (&stack_end)
348 #else
349 # if defined(GNUC) && defined(USE_BUILTIN_FRAME_ADDRESS)
350 # define SET_STACK_END VALUE *stack_end = __builtin_frame_address(0)
351 # else
352 # define SET_STACK_END VALUE *stack_end = alloca(1)
353 # endif
354 # define STACK_END (stack_end)
355 #endif

(gc.c)

`SET_STACK_END`が䞉通りあるので、たず䞀番䞋の堎合から。`alloca()`はスタッ
クの先端に領域を割り圓おお返すので、その返り倀ずスタックの先端アドレス
はかなり近いはずだ。そこで`alloca()`の返り倀でスタック先端の近䌌ずする。

次に戻っお䞀番䞊を芋よう。マクロ`C_ALLOCA`が定矩されおいる堎合は
`alloca()`がネむティブで定矩されおない  ぀たり、互換関数がCで定矩され
おいるこずを瀺す。その堎合は`alloca()`は内郚で`malloc()`でメモリを確保しお
いるのであった。それではスタックの䜍眮を取るのには党く圹に立たない。そ
こでどうするかずいうず、いた実行䞭の関数のロヌカル倉数(`stack_end`)が
スタックの先端に近いず刀断しおそのアドレスを䜿う(`&stack_end`)。

たたこのコヌドには、䜕に䜿っおいるのかよくわからない`alloca(0)`も入っお
いる。これはCで定矩した`alloca()`の昔からの仕様で、いらない領域をチェッ
クしお解攟しおね、ずいう意味である。ちょうどGCをやっおいるから
`alloca()`の割り圓おた分も䞀緒に解攟しおやろうずいうわけだ。しかしそれ
ならそれでこんなずころに玛れこたさず別のマクロに入れおおいたほうがいい
ず思うのだが  。

そしお最埌に真ん䞭の堎合、`builtin_frame_address()`に぀いお。
`
GNUC__`は`gcc`(GNUのCコンパむラ)で定矩されるシンボルである。
それを䜿っお限定しおいるのだから、
これは`gcc`組み蟌みの拡匵呜什だ。`builtin_frame_address(n)`で n 個前の
スタックフレヌムのアドレスが取れる。`
builtin_frame_address(0)`なら
珟圚のフレヌムのアドレスだ。

`rb_gc_mark_locations()`

最埌は実際にスタックをマヌクする関数`rb_gc_mark_locations()`である。

▌ `rb_gc_mark_locations()`


513 void
514 rb_gc_mark_locations(start, end)
515 VALUE *start, *end;
516 {
517 VALUE *tmp;
518 long n;
519
520 if (start > end) {
521 tmp = start;
522 start = end;
523 end = tmp;
524 }
525 n = end – start + 1;
526 mark_locations_array(start,n);
527 }

(gc.c)

基本的には領域をマヌクする関数`mark_locations_array()`に任せればよい。
この関数がやるのは匕数をうたく調節するこずである。このような調敎が
必芁になるのは、マシンスタックが䌞びる方向が決たっおいないからだ。
䜎䜍アドレスに䌞びる堎合は`end`のほうが小さいし、高䜍アドレスに䌞びる
堎合は`start`のほうが小さい。だからアドレスの小さいほうが`start`になる
ようにここで揃えるのだ。

その他のルヌトオブゞェクト

最埌にむンタプリタ組みこみの`VALUE`コンテナをマヌクする。

â–Œ その他のルヌト


1159 /* 登録されおいるグロヌバル倉数をマヌク /
1160 for (list = global_List; list; list = list→next) {
1161 rb_gc_mark(
list→varptr);
1162 }
1163 rb_mark_end_proc();
1164 rb_gc_mark_global_tbl();
1165
1166 rb_mark_tbl(rb_class_tbl);
1167 rb_gc_mark_trap_list();
1168
1169 /* true、falseなどのむンスタンス倉数があればそれをマヌク /
1170 rb_mark_generic_ivar_tbl();
1171
/
rubyのパヌサで䜿う倉数をマヌク(パヌス䞭のみ) */
1172 rb_gc_mark_parser();

(gc.c)

Cのグロヌバル倉数に`VALUE`を入れる堎合は`rb_gc_register_address()`で
そのアドレスをナヌザに登録しおもらうこずになっおいる。`global_List`に
それが保存されおいるので、党郚マヌクする。

`rb_mark_end_proc()`はRubyの`END`文などで登録した、
プログラムの終了時に実行される
手続きオブゞェクトのマヌク(本曞では`END`文は扱わない)。

`rb_gc_mark_global_tbl()`はグロヌバル倉数のテヌブル`rb_global_tbl`の
マヌク(次章『倉数ず定数』参照)。

`rb_mark_tbl(rb_class_tbl)`は前章でやった`rb_class_tbl`のマヌク。

`rb_gc_mark_trap_list()`はRubyの関数メ゜ッド`trap`で登録した
手続きオブゞェクトのマヌク(シグナル関係。これも本曞では扱わない)。

`rb_mark_generic_ivar_tbl()`は`true`などの非ポむンタ`VALUE`のために
甚意されたむンスタンス倉数テヌブルをマヌクする。

`rb_gc_mark_parser()`はパヌサのセマンティックスタックをマヌクする
(セマンティックスタックに぀いおは第二郚を参照)。

ここたででマヌクフェむズは終わりだ。

Sweep

`NODE`の特別扱い

スむヌプフェむズはマヌクされおいないオブゞェクトを探しお解攟しおいく䜜
業だ。しかし、ちょっず理由があっお`T_NODE`型のオブゞェクトだけは特別扱い
されおいる。次のずころを芋おほしい。

â–Œ `gc_sweep()`冒頭


846 static void
847 gc_sweep()
848 {
849 RVALUE p, *pend, *final_list;
850 int freed = 0;
851 int i, used = heaps_used;
852
853 if (ruby_in_compile && ruby_parser_stack_on_heap()) {
854 /
yaccのスタックがマシンスタック䞊にない堎合は
855 パヌス䞭はNODEを回収しおはならない */
856 for (i = 0; i < used; i++) {
857 p = heaps[i]; pend = p + heaps_limits[i];
858 while (p < pend) {
859 if (!(p→as.basic.flags & FL_MARK) &&
BUILTIN_TYPE(p) == T_NODE)
860 rb_gc_mark((VALUE)p);
861 p++;
862 }
863 }
864 }

(gc.c)

`NODE`はパヌサでプログラムを衚珟するために䜿うオブゞェクトだ。`NODE`はコン
パむル䞭には`yacc`ずいうツヌルの甚意するスタックに眮かれるのだが、そのス
タックはマシンスタック䞊にあるずは限らない。具䜓的に蚀うず、
`ruby_parser_stack_on_heap()`が停だずマシンスタック䞊にないこずを瀺す。
するずその堎合は生成途䞭の`NODE`がうっかり回収されおしたう危険があるので、
コンパむル䞭(`ruby_in_compile`)は`T_NODE`型のオブゞェクトを無条件に
マヌクしお、回収されないようにするのである。

ファむナラむザ

ここたで来たらマヌクされおいないオブゞェクトは党お解攟できるようになる。
が、解攟前にもう䞀仕事しなければならない。Rubyではオブゞェクトの解攟を
フックできるようになっおいるので、これを呌ぶ必芁がある。このフックを
ファむナラむザ(finalizer)ず蚀う。

â–Œ `gc_sweep()`䞭盀


869 freelist = 0;
870 final_list = deferred_final_list;
871 deferred_final_list = 0;
872 for (i = 0; i < used; i++) {
873 int n = 0;
874
875 p = heaps[i]; pend = p + heaps_limits[i];
876 while (p < pend) {
877 if (!(p→as.basic.flags & FL_MARK)) {
878 (A) if (p→as.basic.flags) {
879 obj_free((VALUE)p);
880 }
881 (B) if (need_call_final && FL_TEST(p, FL_FINALIZE)) {
882 p→as.free.flags = FL_MARK; /* マヌクされたたた残る /
883 p→as.free.next = final_list;
884 final_list = p;
885 }
886 else {
887 p→as.free.flags = 0;
888 p→as.free.next = freelist;
889 freelist = p;
890 }
891 n++;
892 }
893 © else if (RBASIC→flags == FL_MARK) {
894 /
ファむナラむズが必芁なオブゞェクト。 /
895 /
䜕もしないで攟っおおく */
896 }
897 else {
898 RBASIC→flags &= ~FL_MARK;
899 }
900 p++;
901 }
902 freed += n;
903 }
904 if (freed < FREE_MIN) {
905 add_heap();
906 }
907 during_gc = 0;

(gc.c)

オブゞェクトヒヌプを端から党お芋おゆき、`FL_MARK`フラグが立っおいなかっ
たら`obj_free()`で解攟する(A)。`obj_free()`では䟋えば文字列オブゞェクトが
䜿う`char[]`や配列オブゞェクトが䜿う`VALUE[]`を解攟するだけで、
`RVALUE`構造䜓を解攟したりはないし、`basic.flags`も党くいじらない。だ
から`obj_free()`を呌んだあずにその構造䜓を操䜜しおも萜ちる心配はない。

オブゞェクトを解攟したあず、`FL_FINALIZE`フラグによっお分岐する(B)。
`FL_FINALIZE`が立っおいたらそのオブゞェクトに察しおファむナラむザが定矩
されおいるので`final_list`に、立っおいなかったらすぐに`freelist`に远加す
る。たたファむナラむズするずきは`basic.flags`を`FL_MARK`にする。これで構造
䜓型フラグ(`T_STRING`など)がクリアされるので、生きおいるオブゞェクトず
区別が付く。

あずはたずめおファむナラむザを実行すれば終わりだ。ここで、ファむナラむ
ザを呌ぶずきはフック察象ずなったオブゞェクトは既に死んでいるこずに泚
意しよう。぀たりファむナラむザ実行䞭に、フックをかけたオブゞェクトを䜿
うこずはできない。

â–Œ `gc_sweep()`残り


910 if (final_list) {
911 RVALUE *tmp;
912
913 if (rb_prohibit_interrupt || ruby_in_compile) {
914 deferred_final_list = final_list;
915 return;
916 }
917
918 for (p = final_list; p; p = tmp) {
919 tmp = p→as.free.next;
920 run_final((VALUE)p);
921 p→as.free.flags = 0;
922 p→as.free.next = freelist;
923 freelist = p;
924 }
925 }
926 }

(gc.c)

埌半の`for`がメむンのファむナラむズ䜜業だ。前半の`if`は様々な理由により
Rubyプログラムに実行を移せない堎合だ。ここでファむナラむズを遅らせた
オブゞェクトは先皋のリストの経路©に出おくる。

`rb_gc_force_recycle()`

最埌に少し違う話をしよう。ここたでは`ruby`のガヌベヌゞコレクタがオブゞェクトを回収する
かしないか決めおいたが、ナヌザから明瀺的に回収させるこずもできる。そ
れが`rb_gc_force_recycle()`である。

▌ `rb_gc_force_recycle()`


928 void
929 rb_gc_force_recycle(p)
930 VALUE p;
931 {
932 RANY→as.free.flags = 0;
933 RANY→as.free.next = freelist;
934 freelist = RANY;
935 }

(gc.c)

仕組みはたいしたこずはないが、第二郚・第䞉郚で䜕床か出䌚うこずになるので
玹介しおおいた。

Closing Thoughts

領域の解攟

個々のオブゞェクトで割りあおた領域、䟋えば`String`の`char[]`など、はスむヌ
プフェむズの䞭で解攟されおいたが、`RVALUE`構造䜓自䜓を解攟するコヌドは出
おこなかった。たたオブゞェクトヒヌプでも䜿っおいる構造䜓の数の管理など
はやっおいない。ずいうこずは、`ruby`のオブゞェクト領域は䞀床割り圓おたら
絶察に解攟されないのだ。

䟋えば筆者がいた䜜っおいるメヌラは500通のメヌルのスレッドを構築する
ずき䞀時的に40Mバむトくらい領域を䜿うのだが、そのあずGCされお倧半を䜿わなく
なったずしおもずっず40Mバむト占有し続ける。筆者のマシンもむマドキのや぀なの
で40Mバむトくらい䜿われたずころでなんずもないが、ずっず起動しっぱなしの
サヌバなどでこれが起きるず問題になるこずもあるだろう。

ただし`free()`すればメモリ䜿甚量が枛るずも限らないこずには留意すべきであ
る。メモリをOSに返さない限りプロセスのメモリ䜿甚量は枛らない。そしお
`malloc()`の実装によっおは`free()`しおもメモリがOSに返されないこずはよく
ある。

  ず曞いおいたのだが、なんず本曞の締切間際に`RVALUE`が解攟されるように
なっおしたった。添付CD-ROMには最新版の`ruby`も入っおいるから`diff`しお
芋おみおほしい。  なんお酷いオチだ。

䞖代別GC

マヌク&スむヌプには「オブゞェクト領域党䜓を最䜎でも䞀床なめる必芁が
ある」ずいう匱点があった。䞖代別GCずいう考えかたを䜿うずその匱点を補え
る可胜性がある。

䞖代別GCの基瀎になるのは「ほずんどのオブゞェクトは寿呜が非垞に長いか
非垞に短いかのどちらかである」ずいう経隓則だ。この点は自分の曞くプロ
グラムのこずをちょっず考えおみれば玍埗がいくず思う。

さお、この芏則を螏たえお考えおみるず「長生きするオブゞェクトは毎回毎回
マヌクしおスむヌプしなくおもいいじゃないか」ずいう発想が出おくる。この
オブゞェクトは長生きだな、ず思ったら、特別扱いにしおGC察象から倖せばい
いのだ。するずマヌクにしおもスむヌプにしおもオブゞェクトの数を圧倒的に
枛らすこずができる。䟋えば特定のGCのタむミングで長生きしおいるオブゞェ
クトが半分を占めおいるずすれば察象オブゞェクト数は半分になる。

ただ䞀぀問題がある。䞖代別GCはオブゞェクトを移動できないず非垞にやりに
くいのだ。なぜかずいうず、長生きするオブゞェクトは先皋曞いたずおり「特
別扱い」しないずいけないからである。䞖代別GCは扱うオブゞェクトを枛らし
おコストを䞋げるわけだから、この二぀の䞖代にあるオブゞェクトをきっちり
分類しおおかないず結局のずころ䞡方を察象にするのず倉わらなくなっおした
う。たたさらに`ruby`のGCはconservative GCであるから、
`is_pointer_to_heap()`が動くようにも䜜らなければならない。これが難しい。

そこでどうやっおこの問題を解決するかだが  朚山真人さんの手によっお
`ruby`のための䞖代別GCの実装が公開されおいる。以䞋はこのパッチが各皮の
問題にどう察凊したのかを簡単に瀺しおいくこずにしよう。たた今回は
朚山さんのご厚意により、この䞖代別GCパッチず論文を添付CD-ROMに収録しお
いる\footnote{朚山版䞖代別GCパッチに぀いおは添付CD-ROMの`doc/generational-gc.html`をたず参照のこず}。

では説明に入る。説明がしやすいように、
長生きするオブゞェクトを「旧䞖代オブゞェクト」、
短い寿呜のオブゞェクトを「新䞖代オブゞェクト」
ず呌ぶこずにしよう。

最初に、最倧の問題である旧䞖代オブゞェクトの特別扱いに぀いお。この点は
新䞖代のオブゞェクトだけを`newlist`ずいうリンクリストに぀なぐこずで解決
しおいる。たたこのリストは`RVALUE`の芁玠を増やすこずで実珟する。

第二に、旧䞖代のオブゞェクトを芋付ける方法に぀いお。これはずおも簡単で、
`newlist`でGCされなかったものを`newlist`から倖すだけだ。぀たり䞀回GCを生き
残るず旧䞖代のオブゞェクトずしお扱われる。

第䞉に、旧䞖代から新䞖代ぞの参照を怜出する方法に぀いお。䞖代別GCでは、
蚀っおみれば、旧䞖代のオブゞェクトにはマヌクが付きっぱなしの状態になる
わけだ。しかし旧䞖代から新䞖代ぞリンクが出おいる堎合、その新䞖代の
オブゞェクトにはマヌクが付かなくなる(図11)。

䞖代を越えた参照

これではたずいので、旧䞖代のオブゞェクトから新䞖代のオブゞェクトを参照
したらその瞬間にその新䞖代のオブゞェクトは
旧䞖代にならなければいけない。そこでラむブラリを修正し、こういう
参照が起こる可胜性のあるずころにひたすらチェックを入れるようにしおいる。

仕組みの抂芁は以䞊である。このパッチは圓初`ruby` 1.7に取りこたれる予定だっ
たのだが結局ただ取りこたれおいない。「速床が出なかった」ずいうのが理由
だそうだ。第䞉点の「参照党チェック」のコストが効いおいるのではないか、
ずいう掚枬もなされおいるが、詳しい原因はただよくわかっおいない。

コンパクション

`ruby`のGCでコンパクションはできるだろうか。`ruby`の`VALUE`は
構造䜓ぞの盎ポ
むンタであるから、コンパクションしお構造䜓のアドレスが倉わったら、
移動した構造䜓を指しおいる`VALUE`を党お曞き換えないずいけない。

ずころが`ruby`のGCはconservative GCなので「それが本圓に`VALUE`かどうかわか
らない堎合」がありうる。それなのにその倀を曞き換えおしたったら、もし
`VALUE`でなかったずきにはずんでもないこずになる。コンパクションず
conservative GCはずおも盞性が悪いのだ。

だがなんずかしお察策を考えおみよう。たず`VALUE`をポむンタでなく
オブゞェクトIDに
する方法が考えられる(図12)。`VALUE`ず構造䜓の間に䞀
枚間接局を挟む方法だ。これなら`VALUE`を曞き換えずに枈むので安心しお構
造䜓を移動できる。だがその代償ずしおアクセス速床は遅くなるし、拡匵ラむ
ブラリの互換性もなくなる。

オブゞェクトID経由での参照

そこで次の方法ずしお、「確実に`VALUE`である」ポむンタ「だけ」から
指されおいる構造䜓に限定しお移動するずいう手法がある(図13)。
この手法をMostly-copying garbage collectionず蚀う。普通のプログ
ラムなら`is_pointer_to_heap()`が真になるオブゞェクトはそうたくさんはない
から、かなりの確率でオブゞェクト構造䜓を移動できるこずになる。

Mostly-copying garbage collection

さらにさらに、もし構造䜓が移動できるずいうこずになれば、
同時に䞖代別GCの実装も簡単になる。挑戊しおみる䟡倀はありそうだ。

GC察策の`volatile`

スタック䞊の`VALUE`はGCが面倒を芋おくれるず曞いた。それならば
ロヌカル倉数ずしお`VALUE`を眮いおおけばその`VALUE`は確実にマヌクされる
はずである。しかし珟実には最適化の圱響で倉数が消えおしたうこずがある。
䟋えば次のような堎合は消える可胜性がある。

VALUE str;
str = rb_str_new2("...");
printf("%s\n", RSTRING(str)->ptr);

このコヌドでは`str`自䜓にアクセスしおいないので、コンパむラによっおは
`str→ptr`だけメモリに残しお`str`は消しおしたうこずがある。そうするず
`str`が回収されお萜ちる。こういう時は仕方がないので、

volatile VALUE str;

ずする。`volatile`はCの予玄語で、この倉数に関する最適化を犁止する
効果がある。Ruby関係のコヌドで`volatile`が付いおいたらたず間違いなく
GC察策ず思っおよい。K&Rを読んだずきは「こんなもの䜕に䜿うんだろう」
ず思っおいたのだが、たさか`ruby`でこんなに倧量に芋るこずになるずは
思わなかった。

しかしこういうずころを芋るずconservative GCの「ナヌザがGCを気にしなく
おいい」ずいう謳い文句もあたり圓おにならないようである。䞀時は
「KSMずいうSchemeのGCは`volatile`が必芁ないらしい」
ずいう話もあったのだが、
アルゎリズムに穎があっお結局`ruby`には適甚できないようだ。

起動のタむミング

`gc.c`内郚

When will GC start?
There are three places where `rb_gc() ` is called internally of `gc.c`.

  • `ruby_xmalloc()`
  • `ruby_xrealloc()`
  • `rb_newobj()`

`ruby_xmalloc()`ず`ruby_xrealloc()`の堎合はメモリ割り圓おに倱敗したずきだ。
そこでGCすればメモリが解攟されおたた䜿えるスペヌスができるかもしれない。
`rb_newobj()`も状況は䌌おいお、`freelist`が空になったずきに起動する。

むンタプリタ内

`gc.c`以倖でもむンタプリタ内で`rb_gc()`を呌んでいるずころが䜕か所かある。

たず`io.c`ず`dir.c`で、ファむルディスクリプタが足りなくお開けなかったずき
にGCを起動する。`IO`オブゞェクトがGCされればファむルがクロヌズされお
ファむルディスクリプタが空くかもしれない、ずいう目論芋からだ。

`ruby.c`ではファむルをロヌドしたあずで`rb_gc()`するこずがある。スむヌプの
ずころで曞いたずおり、コンパむル䞭に`NODE`をGCできないのを補うためである。

Object Creation

GCの話が終わっおRubyオブゞェクトの生成から解攟たでを扱えるように
なったので、ここでオブゞェクトの生成に぀いおの話をしおおこう。これは
GCずはあたり関係なく、むしろ前章でやったクラスの話に少し関っおくる。

アロケヌションフレヌムワヌク

これたで䜕床もオブゞェクトを生成しおきた。䟋えばこんな方法がある。

class C
end
C.new()

このずき`C.new`はどうやっおオブゞェクトを生成しおいるのだろうか。

たず`C.new`は実際には`Class#new`である。その実䜓はこうだ。

▌ `rb_class_new_instance()`


725 VALUE
726 rb_class_new_instance(argc, argv, klass)
727 int argc;
728 VALUE *argv;
729 VALUE klass;
730 {
731 VALUE obj;
732
733 obj = rb_obj_alloc(klass);
734 rb_obj_call_init(obj, argc, argv);
735
736 return obj;
737 }

(object.c)

`rb_obj_alloc()`は`klass`に察しお`allocate`ずいうメ゜ッドを呌ぶ。぀た
りいた説明しおいる䟋ならば`C.allocate`を呌ぶ。
そのデフォルトは`Class#allocate`で、そのたた実䜓が
`rb_class_allocate_instance()`である。

▌ `rb_class_allocate_instance()`


708 static VALUE
709 rb_class_allocate_instance(klass)
710 VALUE klass;
711 {
712 if (FL_TEST(klass, FL_SINGLETON)) {
713 rb_raise(rb_eTypeError,
“can’t create instance of virtual class”);
714 }
715 if (rb_frame_last_func() != alloc) {
716 return rb_obj_alloc(klass);
717 }
718 else {
719 NEWOBJ;
720 OBJSETUP;
721 return (VALUE)obj;
722 }
723 }

(object.c)

最埌の䞉行以倖は気にしなくおいい。この`NEWOBJ`はこれたで
も䜕回か出おきた、Rubyのオブゞェクトを䜜るずきのむディオムである。今床
は䞭身も芋おみよう。

▌ `NEWOBJ`


274 #define NEWOBJ type obj = (type)rb_newobj()
275 #define OBJSETUP do {\
276 RBASIC→flags = (t);\
277 RBASIC→klass = ©;\
278 if (rb_safe_level() >= 3) FL_SET(obj, FL_TAINT);\
279 } while (0)

(ruby.h)

`rb_newobj()`は`RVALUE`を䞀぀`freelist`から倖しお返しおくれる関数だった。
`NEWOBJ`に型のごたかしを加えたものにすぎないずわ
かる。たた`OBJSETUP()`は`struct RBasic`郚分を初期化するマクロである。
こちらは`FL_TAINT`フラグを立おるのを忘れないためだけにあるず思っお
いいだろう。

あずは`rb_class_new_instance()`に戻っお`rb_obj_call_init()`を呌ぶこずにな
る。この関数が䜜ったばかりのオブゞェクトに察しお`initialize`を呌び出しお
初期化は完了である。

たずめるず以䞋のようになっおいる。

SomeClass.new            = Class#new (rb_class_new_instance)
    SomeClass.allocate       = Class#allocate (rb_class_allocate_instance)
    SomeClass#initialize     = Object#initialize (rb_obj_dummy)

クラスメ゜ッドの`allocate`が物理的な初期化、`initialize`が論理的な初期
化ず蚀っおもいい。このような仕組み  ぀たりオブゞェクト生成を
`allocate`・`initialize`に分割し、`new`が統蜄するずいう仕組みを、
「アロケヌションフレヌムワヌク」ず呌んでいる。

ナヌザ定矩オブゞェクトの生成

次は拡匵ラむブラリで定矩したクラスのむンスタンス生成に぀いお芋おいく。
ナヌザ定矩ず蚀うからにはその構造䜓は決たっおいないわけで、その割り圓お
かたを教えおあげないず`ruby`にはどうやっお䜜っおいいのかわからない。それ
を教える方法を芋よう。

`Data_Wrap_Struct()`

ナヌザ定矩だろうずなんだろうず生成の仕組み自䜓はアロケヌションフレヌム
ワヌクに埓えばいい。぀たり新しいクラス`SomeClass`をCで定矩するずきは
`SomeClass.allocate`ず`SomeClass#initialize`の䞡方をオヌバヌラむドする。

たずは`allocate`のほうから芋おいこう。ここでは物理的な初期化をする。
䜕を割り圓おればよいかず蚀うず、ナヌザ定矩クラスのむンスタンスは
`struct RData`ず、こちらで甚意する構造䜓の組であった。仮にその構造䜓を
`struct my`型ずしおおこう。その`struct my`を元に`VALUE`を䜜るには
`Data_Wrap_Struct()`ずいうマクロを䜿う。䜿いかたはこうだ。

struct my *ptr = malloc(sizeof(struct my));  /* 適圓にヒヌプに取る */
VALUE val = Data_Wrap_Struct(data_class, mark_f, free_f, ptr);

`data_class`が`val`の所属するクラスで、`ptr`がラップしようずしおいるポ
むンタだ。`mark_f`がこの構造䜓をマヌクするための関数(ぞのポむンタ)。
ず蚀っおも`ptr`自䜓をマヌクするわけではもちろんなくお、`ptr`の指す構造
䜓の䞭に`VALUE`があるずきに䜿うものだ。䞀方の`free_f`は`ptr`自䜓を解攟
するために䜿う関数である。どちらの関数も匕数は`ptr`だ。このあたりは少
し戻っおマヌクのコヌドを読んでもらえるず䞀発で玍埗できるず思う。

`Data_Wrap_Struct()`の䞭身も芋おおこう。

▌ `Data_Wrap_Struct()`


369 #define Data_Wrap_Struct(klass, mark, free, sval) \
370 rb_data_object_alloc(klass, sval, \
(RUBY_DATA_FUNC)mark, \
(RUBY_DATA_FUNC)free)

365 typedef void (RUBY_DATA_FUNC) _((void));

(ruby.h)

`rb_data_object_alloc()`にほずんど委譲されおいる。

▌ `rb_data_object_alloc()`


310 VALUE
311 rb_data_object_alloc(klass, datap, dmark, dfree)
312 VALUE klass;
313 void *datap;
314 RUBY_DATA_FUNC dmark;
315 RUBY_DATA_FUNC dfree;
316 {
317 NEWOBJ;
318 OBJSETUP;
319 data→data = datap;
320 data→dfree = dfree;
321 data→dmark = dmark;
322
323 return (VALUE)data;
324 }

(gc.c)

なんおこずはない。通垞のオブゞェクトず同じく`NEWOBJ`を䜿っお
`RVALUE`を甚意し、メンバを入れるだけだ。

ここで`allocate`の話に戻ろう。ここたでで`VALUE`は䜜れたので、あずはこれを
適圓な関数に入れお`rb_define_singleton_method()`でクラスに定矩しお
やればよい。

`Data_Get_Struct()`

次は`initialize`だ。`initialize`に限らず、メ゜ッドではさっき䜜った`VALUE`か
ら`struct my*`を取り出す方法が必芁になるはずだ。そのためには
`Data_Get_Struct()`ずいうマクロを䜿う。

▌ `Data_Get_Struct()`


378 #define Data_Get_Struct(obj,type,sval) do {\
379 Check_Type(obj, T_DATA); \
380 sval = (type*)DATA_PTR(obj);\
381 } while (0)

360 #define DATA_PTR(dta) (RDATA→data)

(ruby.h)

芋おの通り、`RData`のメンバから(`struct my`ぞの)ポむンタを取り出すだけ
である。簡単だ。`Check_Type()`は構造䜓型のチェックをするだけ。

アロケヌションフレヌムワヌクの問題点

ず、ここたで䜕食わぬ顔で説明しおきたのだが、実は珟圚のアロケヌションフ
レヌムワヌクには臎呜的な問題がある。いた、`allocate`で䜜ったオブゞェクト
が`initialize`やその他のメ゜ッドに出おくる、ずいうこずを蚀ったが、ここで
同じクラスの`allocate`で䜜ったオブゞェクトが枡っおきおくれないず非垞に困
るはずだ。䟋えばデフォルトの`Object.allocate`(`Class#allocate`)で䜜った
オブゞェクトが`String`のメ゜ッドに枡っおしたったらずおも困る。なぜなら
`String`のメ゜ッドは`struct RString`の構造䜓がもらえるず仮定しお曞いおある
のに、実際には`struct RObject`だからだ。こういうこずを防ぐためには、
`C.allocate`で䜜ったオブゞェクトなら、`C`か、その䞋䜍クラスのメ゜ッドだけ
に枡るようにしなければならない。

もちろん普通にやっおいればそうなる。`C.allocate`ならクラス`C`のむンスタン
スを䜜るわけだから、クラス`C`のメ゜ッド以倖には枡らないはずだ。
䟋倖ずしお`Object`のメ゜ッドには枡る可胜性があるが、
`Object`のメ゜ッドは構造䜓型に䟝存しないよう曞いおある。

だが普通にやらなかったらどうだろうか。`C.allocate`はRubyレベルに露出しお
いるので、ただ説明しおいないが`alias`だの`super`だのを掻甚したくるず
`allocate`の定矩を別のクラスに移怍できおしたう。そうするず、クラスは
`String`なのに本圓の構造䜓型は`struct RObject`、なんおオブゞェクトも
䜜れおしたう。぀たりRubyレベルから奜き攟題`ruby`を萜ずせるのだ。これは
困る。

問題の根源は`allocate`がメ゜ッドずしおRubyレベルに露出しおいるこずだ。
逆に蚀うず`allocate`の䞭身をメ゜ッド以倖の手段でクラスに定矩しおおけば
いい。そこで

rb_define_allocator(rb_cMy, my_allocate);

ずいう感じの代替案が珟圚議論されおいる。


埡意芋・埡感想・誀殖の指摘などは
青朚峰郎 <aamine@loveruby.net>
たでお願いしたす。

『Ruby゜ヌスコヌド完党解説』
はむンプレスダむレクトで埡予玄・埡賌入いただけたす (曞籍玹介ペヌゞぞ飛びたす)。

Copyright © 2002-2004 Minero Aoki, All rights reserved.