Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

ARC のメモリ解放タイミングを調べた

More than 5 years have passed since last update.

一つの関数内で容量の大きなファイルを読み込み加工する処理を連続して行っていたらメモリが足りなくなった。

ARC ではスコープを外れ(て参照カウンタがゼロになっ)たオブジェクトは、すぐに破棄されると思っていたのでしばらくハマった。

問題のソース(ARC使用)

ローカルでもWebでも何でもいいけど、ファイルから無視できない程度の容量のデータの読み込みを繰り返す処理。

- (IBAction)buttonDownWithArc:(id)sender {
    NSString* path = @".../bigdata.img";

    for (int i = 0; i < 10000; i++) {
        NSData* data = [NSData dataWithContentsOfFile:path];
        [NSThread sleepForTimeInterval:0.5];
        data = nil;
    }
}

これを Instruments でプロファイルするとこうなる。

arc_memory_release_timing_01.png

じゃんじゃんメモリ確保してしまう(汗
datanil にした時(あるいはスコープ外れた時)にメモリ解放されると思っていたのだが。

ちなみにこの関数の処理が終了すると、メモリが解放される。

非ARC でやってみた

メモリ管理をマニュアルでやったらどうなるかを確認した。

- (IBAction)buttonDownNoArc:(id)sender {
    NSString* path = @".../bigdata.img";

    for (int i = 0; i < 10000; i++) {
        NSData* data = [NSData dataWithContentsOfFile:path];
        [NSThread sleepForTimeInterval:0.5];
        [data dealloc];
        data = nil;
    }
}

この時のメモリ確保状況は、期待した通りになった。

arc_memory_release_timing_02.png

メモリ使用量が線形に 増えない ことが分かる。ARC 利用時にもこうなるようにしたい。

状況は、スコープ内変数の破棄が、関数を抜ける時に遅延されている。
ARC 周りの情報をいろいろ漁っていて、AutoReleasePool との関わりが怪しいと予想した。

より引用:

retain, release, autorelease, deallocはコンパイラのお仕事

ARCを利用する場合、コンパイラが

  • retain, release, autoreleaseを挿入してくれる(自分で呼んではいけない。コンパイラエラーになる)。
  • deallocを適切な位置に挿入してくれる(deallocのオーバーライドは可能。ただし[super dealloc]は不可能)。

ことになります。

コンパイラにより関数単位で @autoreleasepool { } が挿入されているとしたら、最初の図のような動きになるはず。ということは、for ループの中に @autorelease を持ってったらどうか?

ARC + @autoreleasepool

for の中の処理を @autoreleasepool { } で括ってみた。

- (IBAction)buttonDownWithArcAndAutoRelease:(id)sender {
    for (int i = 0; i < 100; i++) {
        @autoreleasepool {
            NSData* data = [NSData dataWithContentsOfFile:_path];
            [NSThread sleepForTimeInterval:0.5];
        }
    }
}

すると、

arc_memory_release_timing_03.png

やたー、期待する動きになったぞ。

まとめ

とここまで調べて、しばらく Obj-C さわってなかったので埃をかぶっていた

を引っ張り出してきて読んだら、P.25 にまさにその事が書かれていて泣いた。

とはいえ、autorelease されたオブジェクトが大量に発生した場合、NSAutoReleasePool のオブジェクトが破棄されない限り、それらのオブジェクトは release されないので、メモリ不足に陥る場合があります。典型的な例は、大量の画像をリサイズしながら読み込む場合でしょう。…

for (int i = 0; i < 画像数; i++) {
    /*
     * 画像読み込み処理
     * autoreleaseされたオブジェクトが大量発生。
     * NSAutoReleasePoolのオブジェクトが破棄されないため
     * いずれメモリ不足発生!
     */        
}

勉強しなおします。。。

amay077
ランチの時は呼ぶといい!
https://blog.amay077.net/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away