メモリの管理

要約:
  • クラスのオブジェクトをインスタンス化する際に、オブジェクトを格納するメモリ領域が割り当てられます。
  • Curl® 言語には、ソース コードに使用するメモリをマイクロ管理するタスクを除去するガーベッジ コレクション システムがあります。
  • ウィーク ポインタを使用して、データをメモリに格納すると同時に、ガーベッジ コレクションやメモリ再割り当てのためにこれを使用することができます。

動的メモリの割り当て

特権の無い Curl アプレットは、メモリが 1GB に制限されています。 「システムの制限 」をご覧ください。 この限度内で、クラスのオブジェクトのインスタンスを作成する時、 1 つのメモリ領域からオブジェクトを格納するメモリが配分されます。 例えば、次のソースコードでインスタンス化されるオブジェクトは、 int と 2 つの char を格納するのに十分なメモリが必要になります。
{define-class public MemoryExample1
  field public a:int
  field public b:char
  field public c:char
}
let myobj:MemoryExample1={MemoryExample1}
インスタンス化されたオブジェクト (前例の myobj など) をポイントするオブジェクト参照変数自体が、インスタンス化されたオブジェクトのメモリ アドレスを指すためのメモリを消費します。これは重要な特徴です。次に例を示します。
{define-class public MemoryExample2
  field public x:int
  field public z:MemoryExample1
}
クラス定義内のフィールド z は、MemoryExample1 クラスのオブジェクトを参照します。MemoryExample2 のインスタンスは MemoryExample1 のインスタンス全体ではなく int およびオブジェクト参照を格納するメモリを割り当てます。
Curl アプレットにより使用されるアドレス空間は、32 および 64 ビットのマシンでは 32 ビットです。

ガーベッジ コレクションによるメモリの解除

C などいくつかの言語では、新しいオブジェクトの作成時にメモリを明示的に割り当てたり、プログラム処理の完了時にメモリを解放する必要があります。プログラム処理時間の大半がメモリの管理に費やされます。
これらの言語では、プログラム処理完了後にメモリを解放しないと、メモリリークが発生します。そうなると、少量のメモリがプログラムで使用されないのでオペレーティング システムで回収されず再使用できなくなります。最終的にはメモリリークによりシステムのメモリが不足することになります。ただし、Curl 言語では次のコードを使用するとその心配はありません。
{define-class public MyClass
  field public a:int
  field public b:char
  field public c:char
}

{value
    let myobj:MyClass={MyClass}
    set myobj={MyClass}
    set myobj={MyClass}
}
このコードにより MyClass クラスの 3 つのオブジェクトが作成されます。オブジェクト参照 myobj は最初の 2 つのインスタンスを参照しないため、これらのインスタンスは非参照またはアクセス不可能になります。したがって、プログラムでインスタンスに再度アクセスできる方法はありません。そのため、オブジェクトは ガーべッジ (無効)になります。
幸いにも、Curl 言語にはガーベッジ コレクション システムが備わっています。定期的に、ガーベッジ コレクタによりメモリ領域がスキャンされ、使用されていないメモリが検索されます。オブジェクト参照が存在しないメモリはガーベッジ コレクタに回収され、後で再度割り当てることができるようになります。
ガーベッジ コレクション システムにより、コードのメモリ使用量を細かく管理する面倒な作業から解放されます。ただし、次に示すガーベッジ コレクションの潜在的な 2 つの短所を考慮する必要があります。

ガーベッジ コレクションの利用

上述の問題点に対処するには、garbage-collect プロシージャを使用して、ガーベッジ コレクションがプログラム内の一定の場所で実行されていることをシステムに示します。不必要なメモリを大量に使用した 1 つのコード後、またはガーベッジ コレクションの停止により問題が発生する可能性がある、処理速度がクリティカルなセクション前に garbage-collect を呼び出します。
次に、garbage-collect を使用した例を示します。Make Garbage をクリックすると、文字が1024個入った配列をインスタンス化しさらに消去することで、少なくとも 1MB のガーベッジが生成されます。Clean Garbage をクリックすると、garbage-collect プロシージャが呼び出され、最初のボタン操作の結果を除去します。
この例の効果を確認するには、Windows NT のタスク マネージャ ウィンドウを使用してシステム上のメモリ使用状況を監視します。Make Garbage をクリックして Curl® 実行環境 (RTE) でのメモリ使用の増加状況を調べます。次に、Clean Garbage をクリックしてメモリ使用の減少状況を確認します。

例: garbage-collectの使用
{value
    let make-garbage:CommandButton =
        {CommandButton label = "Make Garbage",
            {on Action do
                {for x = 1 to 1000 do
                    let a:{Array-of char} = {new {Array-of char}}
                    {a.set-size 1024, 'x'}
                }
            }
        }
    let clean-garbage:CommandButton =
        {CommandButton label = "Clean Garbage",
                {on Action do
                    {garbage-collect}
                }
        }
    {HBox make-garbage, clean-garbage}
}

ウィーク ポインタ

Curl® 言語のアプレット内でメモリ使用を計画するときに、有用でありながらメモリへの格納を絶対に必要としないデータが含まれる場合があります。情報の再読み込みまたは再計算するのではなく、メモリに格納する方法をとることにより、アプリケーションはより高速に実行されます。ただし、システムがメモリ不足で実行されている場合、再使用の可能性がなく、必要に応じて再生成されるような大量のデータを格納するのはかなりの負担になります。
Curl 言語の ウィーク ポインタを使用すると、データをメモリに格納すると同時に、ガーベッジ コレクタが実行された際にデータをガーベッジ コレクションしたり、再割り当てすることができます。使用されないメモリを回収するだけでなく、ガーベッジ コレクタは ウィーク ポインタのみが参照するメモリの回収も行います。
ウィーク ポインタを使用するには、対象のクラスフィールドの宣言時に weak 修飾子を指定します。
{define-class public WeakData
  field private weak _myweakdata:#{HashTable-of String, String}
}
ウィーク ポインタのデータのガーベッジが回収されると、そのデータは null の値を持ちます。したがって、常時 # 演算子を使用してウィーク ポインタが null の値を受け取れることを示す必要があります。# を使用するのを忘れると、コンパイラはエラーを出します。
ウィーク ポインタが参照するオブジェクトに、置換不可能なデータを格納しないでください。ウィーク ポインタは、再計算可能または低いオーバーヘッドで再作成できるデータのみに使用します。データの格納にウィーク ポインタを使用するかどうかを決定する際は、オブジェクトのデータの再計算 (ガーベッジ コレクションされる場合) の必要性やより重要なデータを格納するメモリ スペースの確保を考慮する必要があります。

ウィーク ポインタ データによる処理

ウィーク ポインタが参照するデータがガーベッジ コレクションされると、そのオブジェクト参照変数は null に設定されます。ウィーク ポインタのみが参照するデータにアクセスする前に、オブジェクト参照を調べデータの存在を確認する必要があります。データがなければ、データを再作成する必要があります。アクセッサのみがコンテンツを読み込むので、ウィーク ポインタ対象のフィールドを 常時 private として示す必要があります。
オブジェクト参照を調べて null でないことを確認した後でさえ、作業中にデータはガーベッジ コレクションされることがあります。データにアクセス中にガーベッジ コレクタがそのデータを回収するのを防ぐには、新しい非ウィーク オブジェクト参照を作成する必要があります。オブジェクトを参照する非ウィーク ポインタは、ガーベッジ コレクタがガーベッジを回収するのを防ぎます。キャッシュに入っているデータにアクセス中に、非ウィーク ポインタを削除 (通常、メソッドまたはプロシージャなどそのスコープを出ることにより)することができます。データの非ウィーク参照が完了すると、データは再度ガーベッジ コレクションされます。
ウィーク ポインタが参照するデータに確実にアクセスする方法は次のとおりです。

ウィーク ポインタの例

次の例はウィーク ポインタを使用してデータ キャッシュ クラスを作成する方法を示します。MyCache クラスには HashTable-of String をウィーク参照するプライベート フィールドが含まれます。このクラスのプライベート メソッド make-lots-of-data では、HashTable に格納する必要のあるデータが取り込まれます。クラスのコンストラクタは単にこのプライベート メソッドを呼び出し最初にキャッシュを設定します。
このクラスのみが String パラメータを取り、HashTable 内の該当する文字列を返すメソッドを介してキャッシュ データにアクセスすることができます。 このメソッドはプライベート キャッシュ フィールドが null かどうかをチェックします。null の場合、キャッシュはガーベッジ コレクションされるので make-lots-of-data メソッドを再度呼び出してキャッシュを作成する必要があります。
{define-class public MyCache
  || The field that will hold the cached data
  field private weak _cached-data:#{HashTable-of String, String}
  || The private method that generates the cached data
  {method private {make-lots-of-data}:{HashTable-of String, String}
    || Lots of data generated here, stored in a HashTable and the
    || HashTable is returned.
  }

  || The constructor calls make-lots-of-data to have the cache
  || filled after the object is created. You could defer this to
  || be when the object is first accessed, as well.
  {constructor {default}
    || compute contents of cached-data by calling a private method
    set self._cached-data={self.make-lots-of-data}
  }

  || This method is called when people want to access the
  || cached data. Here, we accept a string and return the
  || associated string from the cached HashTable.
  {method public {get-cache-element index:String}:String
    || Create a temporary reference to the cached data that
    || isn't weak, to prevent the cache from being garbage
    || collected while it is being accessed.
    let tmp-cache-pointer:#{HashTable of String, String}=
        self._cached-data

    {if tmp-cache-pointer == null then
        || It has been garbage collected! Recalculate
        set tmp-cache-pointer = {self.make-lots-of-data}
        || Set the weak cache pointer to the new copy of the
        || cache
        set self._cached-data = tmp-cache-pointer
    }
    || Return the data from the cache. Of course, in actual use,
    || you would perform error checking here to make sure the element
    || exists in the cache! You could also add code to calculate
    || requested elements not already in the cache, and store the
    || results into the cache as well as returning them.
    {return {tmp-cache-pointer.get index}}
    || After exiting this method, the tmp-cache-pointer will no longer
    || exist, and therefore the cache data will again be available
    || for garbage collection.
  }
}