コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

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_MALLOCGC_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周りのソースが、めちゃめちゃきれいになっています。めっちゃ読みやすいと思いますので、一読をお薦めします。