NSAutoreleasePool を使ってメモリ解放

2010年10月4日月曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

autorelease


autorelease を使ってオブジェクトを生成した場合、そのオブジェクトはランループ(イベント処理の周期)終了時に解放される。
(例) NSMutableArray* array = [NSMutableArray array];

通常はこの仕組で問題ないが、バッチ的な処理を1箇所で行なう場合に autoreleaeを使うと解放されない大量の autoreleae属性のオブジェクトが残ってしまう場合がある。
(例) for (i=0; i < 100; i++) {
    NSMutableArray* array = [NSMutableArray array];
      :
    時間のかかる処理
      :
}
これは、処理が終わるまでランループが終了しないので autoreleae属性のオブジェクトを解放するタイミングがこない為に起こる。
これを回避するには autorelease を使うのをやめて自前で releaseする。
(例) for (i=0; i < 100; i++) {
    NSMutableArray* array = [[NSMutableArray alloc] init];
      :
    時間のかかる処理
      :
    [array release];
}

もしくは NSAutoreleasePoolを局所的に作る。
(例) for (i=0; i < 100; i++) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSMutableArray* array = [NSMutableArray array];
      :
    時間のかかる処理
      :
    [pool release];
}
こうするとランループでの解放を待たずに autorelese属性のオブジェクトを解放することができる。NSAutoreleasePool はインスタンスが作成されると、それ以降の autorelease属性のオブジェクトを覚えていて、自分が releaseされるタイミングでこれらのオブジェクトへ releaseを投げて解放する。

なお NSAutorelesePoolで管理される autorelease属性のオブジェクトの一覧はプライベートメソッドを使うと見ることができる。
NSLog(@"%@", [NSAutoreleasePool showPools]);
こんな感じ。
- -- ---- -------- Autorelease Pools -------- ---- -- -
==== top of stack ================
  0x620d860 (__NSArrayM)
  0x6014b20 (NSCFNumber)
  0x6019ae0 (__NSArrayI)
  0x6019a70 (__NSArrayM)
  0x6019b90 (__NSArrayI)
  0x6002f00 (__NSArrayM)
  0x6018030 (__NSArrayI)
  0x60109b0 (__NSCFDictionary)
     :
     :
==== top of pool, 102 objects ================
  0x60172d0 (__NSCFData)
  0x6013210 (__NSCFData)
  0x60130d0 (NSPathStore2)
  0x60130b0 (NSCFString)
  0x6012fd0 (NSCFString)
==== top of pool, 5 objects ================
==== top of pool, 0 objects ================

[参考情報] [NSAutoReleasePool][CFRunLoop] NSAutoReleasePoolの管理者は誰であるべきか - Ni chicha, ni limona - 平均から抜けられない僕 - iPhoneアプリ開発グループ


サンプル


効果を確認する為サンプルを作ってみた。1MBの NSDataを連続で100回作成した時のメモリ利用量を NSLog()でデバッグコンソールへ書き出す。
こんな感じ。
#define BUFF_SIZE (1024*1024)
static char buff[BUFF_SIZE];


- (void)test
{
 for (int i=0; i < 100; i++) {
  
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  [NSData dataWithBytes:buff length:BUFF_SIZE];
  メモリ利用量表示処理
  [pool release];
 }
 NSLog(@"end");
}

(1)NSAutoreleaePool なしの場合、(2)ありの場合、(3)ありで drainを使った場合の3つを調べた。

(1) NSAutoreleasePoolなしの場合

AutoreleasePoolTest[54677:207] RSS: 13.5 MB
AutoreleasePoolTest[54677:207] RSS: 14.8 MB
AutoreleasePoolTest[54677:207] RSS: 15.8 MB
AutoreleasePoolTest[54677:207] RSS: 16.8 MB
AutoreleasePoolTest[54677:207] RSS: 17.8 MB
    :
    :
AutoreleasePoolTest[54677:207] RSS: 110.9 MB
AutoreleasePoolTest[54677:207] RSS: 111.9 MB
AutoreleasePoolTest[54677:207] RSS: 112.9 MB
AutoreleasePoolTest[54677:207] end
forループを1回るたびに 1MB増加しているのがわかる。

(2) NSAutoreleasePoolありの場合(release解放)

AutoreleasePoolTest[54700:207] RSS: 13.5 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
    :
    :
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] end
forループを繰り返しても一定のメモリ利用量から変化しない。ループ内で確保された NSDataはループの終わりできちんと解放されている。

(3) NSAutoreleasePoolありの場合(drain解放)

AutoreleasePoolTest[54700:207] RSS: 13.5 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
    :
    :
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] RSS: 13.8 MB
AutoreleasePoolTest[54700:207] end
release使用時と変わらず。


と、NSAutoreleasePool の効果がよくわかった。


メモリ利用量の取得


現在使用しているメモリ量は task_info() を使うと取得できる。
[参考情報] 自分のアプリが使用しているメモリサイズを取得するには - The iPhone Development Playground

今回のコードはこんな感じ。
struct task_basic_info t_info;
  mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
  
  if (task_info(current_task(), TASK_BASIC_INFO,
       (task_info_t)&t_info, &t_info_count)!= KERN_SUCCESS) {
   NSLog(@"%s(): Error in task_info(): %s",
      __FUNCTION__, strerror(errno));
  }
  
  u_int rss = t_info.resident_size;  
  NSLog(@"RSS: %0.1f MB", rss/1024.0/1024.0);


ソースコード


GitHubからどうぞ。
AutoreleasePoolTest at 2010-10-04 from xcatsan's iOS-Sample-Code - GitHub


参考情報


[NSAutoReleasePool][CFRunLoop] NSAutoReleasePoolの管理者は誰であるべきか - Ni chicha, ni limona - 平均から抜けられない僕 - iPhoneアプリ開発グループ

自分のアプリが使用しているメモリサイズを取得するには - The iPhone Development Playground

NSAutoreleasePool Class Reference

Memory Management Programming Guide: Autorelease Pools
- - - -
release と drain で挙動が違う現象に遭遇した。その為、今回サンプルを作って確認してみたのだがやはり差異はなかった。うーむ。

Responses

Leave a Response

人気の投稿(過去 30日間)