第54回 ガベージコレクションについて

Perlガベージコレクションの話をしたいと思います。


相方から、こんな質問が入りました。

相方の発言

sub h{
 my @c = 1..10;
 return \@c;     # ここは大丈夫?
}

my $d = h();
print $_, "\n" for @$d;

ってできないよね? C言語的な意味で。

私の発言

できる。 Perl的な意味で。



C言語では

それでは、なぜ相方はこんな発言をしたのだろうか。
C言語のソースを見てみよう。


まず、正常なものから、

#include <stdio.h>

int main(void){
  int i[10];
  int j = 0;

  for(j=0;j<10;j++){
    i[j] = j;
  }

  for(j=0;j<10;j++){
    printf("%d \n", *(i+j));
  }

  return 0;
}

結果:

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 


では、つぎのように、配列を作るのは他の関数に任せてみる。

#include <stdio.h>

int* h(void){
  int i[10];
  int j = 0;

  for(j=0;j<10;j++){
    i[j] = j;
  }
  return i;
}

int main(void){
  int *i;
  int j = 0;

  i = h();
  
  for(j=0;j<10;j++){
    printf("%d \n", *(i+j));
  }

  return 0;
}

結果:

0 
-1073743288 
-1828934345 
-1607064072 
-1607078496 
8186 
-1073743276 
7 
8186 
-1073743200 

おお! わけわからないものが表示された!!

実は、コンパイルの段階から次のような警告がでている。

warning: function returns address of local variable

ローカルな変数のアドレス返してまっせ。 ええでっか?
と言っているのである。


そのとおり、これではいけないのだ。
h関数が終わった時点で、 iという名前の配列は解放されてしまっているのだ。
言い換えると、h関数のスコープが終わった時点でiが解放されている。
解放されてしまっているので、もう値を取得できない。


次のようにすれば、h関数のスコープが終わってもメモリ空間は生き続ける

#include <stdio.h>
#include <stdlib.h>

int* h(void){
  int* i = (int*)calloc(10, sizeof(int));
  int j  = 0;

  for(j=0;j<10;j++){
    i[j] = j;
  }
  return i;
}

int main(void){
  int *i;
  int j = 0;

  i = h();
  
  for(j=0;j<10;j++){
    printf("%d \n", *(i+j));
  }
 
  free(i);

  return 0;
}

結果:

0 
1 
2 
3 
4 
5 
6 
7 
8 
9 

詳しいことが知りたい方は、callocかmallocについて調べてみるとよいだろう。


Perlガベージコレクション

第4回 Perlはメモリについて意識しないでいいようにできている - bingo_nakanishiの他言語出身者のためのPerl入門
でも述べたが、Perlはメインメモリについて意識しなくてよいのだ。


そうだ。Javaのようにガベージコレクションしてくれるのである。
ただし、Javaとはガベージコレクションで使われているアルゴリズムは違っている。
ここでは、JavaガベージコレクションPerlガベージコレクションの違いについては述べず、Perlガベージコレクションだけについて述べる。


Perlガベージコレクションについて結論から述べると、

参照をだれかが保持している限り、解放されない

これである。

perlは、その値が確保されているメモリ空間を指している変数の数を内部で数えている。
最初のソースに戻ると、


(再掲)

sub h{
 my @c = 1..10;
 return \@c;     # ここは大丈夫?
}

my $d = h();
print $_, "\n" for @$d;

my $d = h();
が呼ばれた瞬間、$dは、@cがいるメモリ空間をさすので、まだ@cは解放されないのだ。
あとは、@cを参照するものがなくなった時点でうまい具合に@cは解放されるであろう。


だから、C言語のように神経質になる必要はないのである。