Microsoft DirectX 8.0 (C++) |
ロードされたオブジェクトをキャッシングすると、アプリケーションで多くのオブジェクトをロードする場合、特にほかのオブジェクトを参照するオブジェクトをロードする場合に、メモリの浪費につながることがある。
デフォルトの場合のように、自動的なキャッシングが有効である場合、参照によってロードされるオブジェクトを含め、IDirectMusicLoader8::GetObject によってロードされる各オブジェクトはキャッシングされる。たとえば、セグメントについて GetObject を呼び出す場合、このセグメントにスクリプトへの参照が含まれていると、スクリプトもロードされてキャッシングされる。
ただし、IDirectMusicLoader8::ReleaseObject または IDirectMusicLoader8::ReleaseObjectByUnknown を呼び出した場合、GetObject によってロードされたプライマリ オブジェクトだけがキャッシュから削除される。参照されたオブジェクトは、ほかのオブジェクトによって使用中かどうかに関係なく解放されない。
使用されていないオブジェクトをクリーンアップするには、IDirectMusicLoader8::CollectGarbage を呼び出す。このメソッドは、GetObject によって直接ロードされたオブジェクト、およびそのオブジェクトによって参照されるオブジェクトを除いて、すべてのオブジェクトをキャッシュから解放する。CollectGarbage は、ローダーのオブジェクトへの COM 参照を解放することによって、キャッシュからオブジェクトを消去する。この結果、オブジェクトの参照カウントが 0 になった場合は、オブジェクトは破棄され、そのメモリは再び利用可能になる。
ロードされたオブジェクトが不要になったときにメモリ内に残らないようにするには、次の処理を行う必要がある。
オブジェクトが互いに循環参照している場合は処理が複雑になる。セグメントにスクリプト トラック経由でスクリプト オブジェクトへの参照が含まれ、スクリプトにセグメントへの参照が含まれているとする。GetObject を呼び出してセグメントを直接ロードすると、スクリプトが間接的にロードされる。次に、ReleaseObject を使用してキャッシュからセグメントを解放し、アプリケーションのセグメントへの参照について Release を呼び出す。スクリプト オブジェクトによって保持される、セグメントへの COM 参照が存在するので、このセグメントは存在し続ける。この場合、スクリプトはキャッシュ内のほかのオブジェクトによって参照されないので、ガベージになる。ただし、特殊な処理を行わない場合、CollectGarbage はローダーのスクリプトへの参照だけを解放する。このため、その参照カウントは 0 にならない。セグメントとスクリプトは互いに参照し続けるので、両方ともキャッシュから削除されているのに、メモリ内に存在し続ける。
この問題を回避するために、CollectGarbage はオブジェクトについて内部メソッドを呼び出して、そのオブジェクトがほかのオブジェクトへの参照を解放するように強制する。上の例では、スクリプトはセグメントへの参照を解放する。セグメントの参照カウントが 0 になり、セグメント自体を破棄するときに、セグメントはスクリプトへの参照を解放するので、ローダーがスクリプトへの参照を解放するときに、スクリプトはそれ自体を破棄できる。
しかし、ここでもう 1 つ問題がある。アプリケーションが、ローダーが知らないスクリプトへのインターフェイスを取得し、このポインタについての Release の呼び出しを無視したとする。スクリプトは存在し続けるが、意図したとおりに動作しない場合がある。これは、セグメントへの参照が存在しないからである。このスクリプトについてメソッドを呼び出すと、致命的なエラーが発生する場合がある。これを回避するために、CollectGarbage はスクリプトについてのすべてのメソッドが DMUS_S_GARBAGE_COLLECTED を返すことを保証している。
この状況は、ほとんどのアプリケーションに影響しない。ただし、CollectGarbage によってキャッシュから消去されたオブジェクトについてメソッドを呼び出す場合、予期した結果にならない場合があることに注意する必要がある。
次のサンプル コードでは、セグメントへの参照を含むスクリプトをロードしている。m_pLoader は IDirectMusicLoader8 インターフェイスで、m_pPerformance は IDirectMusicPerformance8 インターフェイスである。この例では、スクリプトでルーチンを呼び出した後、キャッシュからスクリプト オブジェクトを削除し、CollectGarbage を呼び出してセグメント オブジェクトを解放している。セグメントにスクリプトへの参照が含まれる場合、このスクリプトを破棄できるようにスクリプトが解放され、次にセグメントが解放されて破棄できるようになる。
// スクリプトをロードし、ルーチンを呼び出す。 IDirectMusicScript8 *pScript; m_pLoader->GetObject(scriptdesc, IID_IDirectMusicScript8, &pScript); pScript->Init(m_pPerformance, NULL); pScript->CallRoutine(L"DoorSlam", NULL); // スクリプト オブジェクトを解放し、ガベージを収集する。 pLoader->ReleaseObjectByUnknown(pScript); pLoader->CollectGarbage(); pScript->Release();