大きなプログラムを組む(4)
2021/07/04
メモリ管理とリファレンスカウント
  • 前回及び前々回レポートにて、Perlのリストとハッシュはメモリ確保や解放を自動で行ってくれることを説明しました。

  • さて、このメモリ管理ですが、Perlに限らず様々なプログラム言語においてメモリ管理とは下記を指します。
    1. 必要なメモリの確保
    2. 確保したメモリの使用
    3. 使用終了したメモリの解放

  • "必要なメモリの確保"と"確保したメモリの使用"は、プログラマが意識して変数やオブジェクトを使っているので、メモリ確保は言語のコンパイラ/インタープリタ側で比較的簡単にやってくれます。

  • 少しわかりにくいのが"使用終了したメモリの解放"です。"使用終了"という判断をどうしているかですが、Perlは"リファレンスカウント(Reference Count)"と呼ばれる方法でメモリ解放を自動化(*1)しています。

オブジェクトの有効範囲
  • 話は変わりますが、原則変数やオブジェクトは、それらが定義された関数やクロージャがスコープ(Scope,有効範囲)であり、スコープが終了すれば変数やオブジェクトに割り当てられたメモリは解放されます...と言いたいところですが、実はPerlの場合デフォルトは「全てグローバルオブジェクト」です。

  • 10行程度の小さなスクリプトであれば、全て(多分数個)のオブジェクトがグローバルでも問題ありませんが、ここで扱うのは"大きなプログラム"なので「原則としてグローバルオブジェクトは使いません」。変数名の管理が難し過ぎますし、コードを構造化しても使用しているオブジェクトがいつ強奪されるかわからないという「嫌すぎる状況」になってしまいます。

  • Perlでは、この対策を下記の二つで行います。
    1. 変数/オブジェクト定義に"my"を使用する。
    2. 更に"use strict;"で"my"の使用を強制する。

  • myを使用して宣言した変数やオブジェクトは、スコープの外部からアクセスできず、更にスコープが終了すると自動で解放されるローカルオブジェクトになります。

  • では"my"の使用を強制されている状況で"グローバルオブジェクト"を使いたい場合ですが、グローバルスコープでオブジェクトを宣言すれば良いです(*2)
    #!/usr/bin/perl -w
    use strict;

    # グローバルスコープ
    my %global_object; # ← myを付けているが、同一ファイル内のクロージャや関数からアクセスできる

    { # クロージャ
      $global_scope{date}='2021/07/04';
      ...
    }

    sub some_function { # 関数
      $global_scope{time}='12:00';
      ...
    }

では改めてリファレンスカウント
  • "use strict;"を適用することで、大抵の場合オブジェクトはローカルになりました。この状況で"ある程度大きなオブジェクトの中身"を他のスコープへ渡すにはリファレンスを経由することになります。大きなプログラムで扱うデータは構造化されているので、その面から考えてもリファレンスの使用は必須になります。

  • リファレンスを使用して他のスコープに値を"参照渡し"する場合、プログラマが"意図"をもって関数への引数としてリファレンスを記述します。

  • このリファレンスは、構造化されたコーディングでは様々な関数やクラスオブジェクトから呼び出されていくわけですが、Perl側でそのリファレンスの参照数をカウントしており、その値が0になったとき、該当オブジェクトは役目を終了したとしてメモリ解放を自動で行ってくれます

  • つまり"use strict;"でローカルオブジェクト使用を強制し、そのオブジェクトをリファレンス経由で扱うと、メモリの使用/解放をプログラマが意図的にコントロールできるようになるわけです。

  • またメモリ管理、特に解放の仕組みを理解していれば、メモリリーク(メモリが解放されずに積み上がる状況)が起きたときでも「どこかリファレンスが残っている?」という観点で問題を調べることができます。例えば循環参照とかですね。

Perlではリファレンスの理解が必須
  • ここまでPerlのリファレンスについて説明してきましたが、その重要性を理解いただけたでしょうか。プログラミングの自由度が一気に上がるはずです。

  • Perlではリファレンスを使用したデータ構造化の自由度が非常に高いため、ハッシュとリストを自分の都合に合わせて組み合わせたオブジェクトを容易に作成することができます。

  • またメモリ解放の仕組みがリファレンスカウントのため、リファレンスを経由した構造化データのハンドリングは、メモリ管理も暗黙で行っていることになります。

  • ここまでが参照/リファレンスに関する説明でした。次回は文字データの処理に便利な正規表現に話を移したいと思います。
Notes
  • 使用終了したデータを自動的に開放する仕組みを一般にガベージコレクション(Garbage Collection)と呼びます。リファレンスカウントは実現手段の一つになります。
  • でも明確な手抜き目的以外にグローバルオブジェクトを使うのであれば、コードの再検討を勧めます。
Copyright(C) 2021 Altmo
本HPについて