Android-4.0.1_r1のソースをダウンロードしてみた
せっかく、Ice Cream Sandwichのソース公開されたので、「よーし、どこよりも早いコードレビューだぁ」ということで、忙しくて全然ソースを見ている時間が無いのですが、ちょっとダウンロードしてみました。
どこが変わっているか、まぁ、一番興味のあるところは、当然、Dalvikくんですよね。ということで見てみましょう。
おぉ、dalvikvmですが、どこが変わったか差分とってみようと思ったのです。そしたら、こんなことに。分かりますよねぇ。.c がすべて.cpp になっています。
そんで、Dalvikくんのソースを見るといえば誰がなんと言っても、やっぱりheapでしょう。ということで新しくなったheap.cppを覗いてみました。
dvmCollectGarbageInternal
先ず、余り時間も無く、また明日から出稼ぎお仕事に行かねばならないので、とにかく見たのは、CGInternalです。v2.2からv2.3になってConcurrent-GCになり、gcDaemonThread内でGCループがぐるんぐるん回っていましたが、どうなったでしょうか?
「v3.0以降は、External Allocationはありまへん。もう、見ることないでぇ」とTimさんが言ってましたが、本当に無くなったのでしょうか?
どうかな?void dvmCollectGarbageInternal(const GcSpec* spec)を見ました。おぉ、dvmCollectGarbageInternal()の引数が、GcSpecというものになっている。これなんだ?
見てみると、GcReasonというenumから進歩したのですね。ちなみに、v1.5〜v2.2まではGcReasonはただの飾りものだったですが、v2.3になって、Concurrent-GCが入ってきたことで、ちゃんとdvmCollectGarbageInternal()の中で意味を持って使われるようになりましたが、さらに進化したようですね。
GcReasonはいずこへ
GcReasonはどうなったのかな?ということで、Heap.hを見てみましょう。enumでは無くて、新たにGcSpec構造体としてGCのモードが4つ宣言されています。const宣言だからenumのまま大文字表記なんですね。なるほど。
今までenumで使っていた GC_FOR_MALLOC、GC_CONCURRENT、GC_EXPLICITは健在ですね。そして、Timさんのおっしゃるとおり、GC_EXTERNAL_ALLOCが無い。これに苦しめられた人はいるのではないでしょうか・・・。もう、Dalvikくんは、Native heapからメモリを確保しないということですよね。違うのかな?そうですよねぇ(除く、Native C)。
ついでに、GC_HPROF_DUMP_HEAPも無くなっちゃってるけど、デバッグのときしか使ってないから消しちゃったのかな。ていうか、実質使ってなかったから要らなくなったんで取っちゃったのかな。
そして、見たことがないGC_BEFORE_OOMが追加されています。説明をみるとOut Of Memoryを出す前の最後の頑張りみたいなときに行うGCみたいですね。Native Heapからmallocすることを無くしたので、GC_EXTERNAL_ALLOCの代わりに、追加されたGCモードのようですね。
/* Not enough space for an "ordinary" Object to be allocated. */ extern const GcSpec *GC_FOR_MALLOC; /* Automatic GC triggered by exceeding a heap occupancy threshold. */ extern const GcSpec *GC_CONCURRENT; /* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */ extern const GcSpec *GC_EXPLICIT; /* Final attempt to reclaim memory before throwing an OOM. */ extern const GcSpec *GC_BEFORE_OOM;
struct GcSpec
上の宣言はGcSpec構造体のポインタなので、v2.3のenumとしてGCの意味だけを渡していたところから、何か追加されたのか? GcSpecは何なの?ということで見てみました。
構造体としては、isPartial、isConcurrent、doPreserve、reasonの4つを持っていますね。reasonは文字列が入るようです。
struct GcSpec { /* If true, only the application heap is threatened. */ bool isPartial; /* If true, the trace is run concurrently with the mutator. */ bool isConcurrent; /* Toggles for the soft reference clearing policy. */ bool doPreserve; /* A name for this garbage collection mode. */ const char *reason; };
しかし、なんのこっちゃ、これを見ただけでは分かりません。実際にデータを突っ込んでいるところを見てみます。
Heap.cppにありました。1つずつ見てみます。GC_FOR_ALLOCはと、なるほど、v2.3のGC_FOR_ALLOCと一緒ですね。Concurrentで無いので、Stop The Worldを短くしたいのでisPartialがtrueなんですね。ここらへんもコードは余り替わっていないようですね。まだ見てないですけど・・・。
static const GcSpec kGcForMallocSpec = { true, /* isPartial */ false, /* isConcurrent */ true, /* doPreserve */ "GC_FOR_ALLOC" }; const GcSpec *GC_FOR_MALLOC = &kGcForMallocSpec;
これはConcurrent-GCですね。あれでもisPartialがtrueですね。v2.3ではConcurrentのときはFullGCしていたのに、FullGCではスレッドを止めている時間が長くなりすぎたのかな。だとすると、isPartialのときの処理もisConcurrentがtrueとfalseで変えているかも知れませんね。
と思って、今、ソースをざっと見てみましたが、ソースめっちゃきれいになっていますね。すごい、どうしちゃったんでしょうか。
Partialの処理は、特にv2.3のときと変わっていないので、GC_CONCURRENTのときもisPartialでループしていて、sweepBitmapCallback()を1回しか回していないですね。
通常のGCではFullGCをあきらめたみたいです。v2.3のときも、Concurrent-GCがぐるんぐるん回っていたので、Concurrent-GCでFullGCしなくてもいいんじゃね?というところがありましたので、ここはサクッとやめたのですね。
static const GcSpec kGcConcurrentSpec = { true, /* isPartial */ true, /* isConcurrent */ true, /* doPreserve */ "GC_CONCURRENT" }; const GcSpec *GC_CONCURRENT = &kGcConcurrentSpec;
さてと、GC_EXPLICITです。こいつは明示的に「GCしろ!」と言われてやるので、堂々とFullGCしても良いよなぁと思っていて、確かに、FullGCしていますが、何と、isConcurrentがtrueです。sweepBitmapCallback()の中もConcurrentで動いているときにはHeapロック管理をきちんとするようにコードを書き直しているようなので、ここまで気を使ってGC_EXPLICITもConcurrentで動かしているのですね。Good-Jobです。
static const GcSpec kGcExplicitSpec = { false, /* isPartial */ true, /* isConcurrent */ true, /* doPreserve */ "GC_EXPLICIT" }; const GcSpec *GC_EXPLICIT = &kGcExplicitSpec;
それで、新たに入ったGC_BEFORE_OOMは、見事に全てfalseですね。doPreserveがfalseだとどうなるかというと、Soft参照を全てGCすることにしていますね。OOMを避けるために、できるだけメモリを開ける努力をしているのですね。ただ逆に、doPreserveがtrueだとSoft参照をGCしないのかというと、そんなことは無いようです。
static const GcSpec kGcBeforeOomSpec = { false, /* isPartial */ false, /* isConcurrent */ false, /* doPreserve */ "GC_BEFORE_OOM" }; const GcSpec *GC_BEFORE_OOM = &kGcBeforeOomSpec;
とりあえず、明日の仕事のためにもう寝ます。さらっと見ただけですが、Dalvikのheap周りのソースが、めちゃめちゃきれいになっています。めっちゃ読みやすいと思いますので、一読をお薦めします。