Palm OS Programmer’s Companion Volume I/5-3 - project-enigma

Palm OS Programmer’s Companion Volume I/5-3

>> Site top >> Misc >> In my palm >> Palm開発ドキュメントの和訳 >> Palm OS Programmer’s Companion Volume I >> Palm OS Programmer’s Companion Volume I/5-3

最終更新日付:2014/01/01 00:00:00

← 2 節に戻る ↑5 章トップへ 4 節に進む →

5-3 メモリマネージャ

 

Palm OS のメモリマネージャは、不揮発性ストレージ、揮発性ストレージ、および ROM にある全てのメモリチャンクの位置とサイズを管理する責任を持っています。メモリマネージャはチャンクの新規アロケート、破棄、リサイズ、ロックおよびアンロック、断片化した場合の圧縮などのための API を提供します。Palm ハンドヘルドは RAM やプロセッサ資源に制限があるため、メモリマネージャは計算資源やメモリの使用に関して効率的になっています。

このセクションでは、Palm OS におけるメモリ管理に関する背景と、メモリマネージャAPI の概要を提供します。以下のトピックがあります。

 

メモリマネージャの構造

このセクションでは、メモリマネージャが使用する以下の構造について説明します。

 

ヒープの構造

IMPORTANT

ヒープの構造は将来変更される可能性があります。ヒープの操作には API を使用して下さい。

ヒープはヒープヘッダ、マスタポインタテーブル、およびヒープチャンク群で構成されています。

ヒープヘッダ

ヒープヘッダはヒープの先頭に配置されます。ヒープのサイズや各種のフラグを保持しており、メモリマネージャに情報 —— 例えばそのヒープがROMベースか、など —— を提供します。

マスタポインタテーブル

ヒープヘッダの次にはマスタポインタテーブルがあります。これはヒープ内の移動可能なチャンクを指す32ビットポインタを格納するのに使用されます。

メモリマネージャがヒープを詰めるためにチャンクを移動する場合、そのチャンクを指すマスタポインタテーブル内のポインタはチャンクの移動先に更新されます。アプリケーションが移動可能なチャンクの参照を追跡するために使用するハンドルは、チャンク自体ではなくマスタポインタテーブル内のアドレスを指しています。これにより、ハンドルはチャンクを移動した後でも有効であり続けます。OS は利用可能な連続領域がアロケートのリクエストに応えられるほど十分でない場合、ヒープを自動的に最適化します。

マスタポインタテーブルが一杯になると、新しいテーブルがアロケートされてそのオフセットが既存のテーブルの nextMstrPtrTable フィールドに保存されます。この方法により、マスタポインタテーブルをいくつでもつなげることができます。追加のマスタポインタテーブルは移動不可能なため、すぐ次の"ヒープチャンク"で説明しているガイドラインに従って、ヒープの最後方にアロケートされます。

ヒープチャンク

マスタポインタテーブルの次には、ヒープの実際のチャンクが配置されます。移動可能なチャンクは通常ヒープの先頭にアロケートされ、移動不可能なチャンクはヒープの後方にアロケートされます。移動不可能なチャンクはメモリマネージャによって再配置されることはありえないため、マスタポインタテーブルにエントリを追加する必要はありません。

チャンクヘッダにはチャンクのサイズが含まれているため、アプリケーションはチャンクからチャンクへ簡単に移動できます。全ての移動可能および移動不可能なチャンクは、この方法でチャンクヘッダのフラグをチェックすることで見つけることができます。

 

ヒープは ROM ベースでもありえるため、ヒープの使用において変更されうるような情報はヘッダには含まれません。また、ROM ベースのヒープには移動不可能なチャンクしか含まれず、マスタポインタテーブルのエントリ数は 0 になります。

 

チャンクの構造

IMPORTANT

チャンクの構造は将来変更される可能性があります。チャンクの操作には API を使用して下さい。

それぞれのチャンクは 8 バイトのヘッダ情報で始まり、チャンクのデータがそれに続きます。チャンクヘッダは Flags:size 調整バイト、3 バイトのサイズ情報、lock:ownerバイト、3 バイトの hOffset 情報で構成されています。

※訳注

以下に登場するニブル(nibble)という用語は、1バイトの半分(すなわち 4 ビット)を意味しています。

Flags:sizeAdj バイト

このバイトは、上位ニブルにフラグがあり、下位ニブルにはサイズ調整情報があります。フラグのニブルは現在1ビットだけが定義されています。そのビットは移動可能なチャンクの場合にセットされます。サイズ調整ニブルは与えられた実際のサイズからチャンクの要求サイズを計算するのに使用されます。要求サイズはチャンクヘッダのサイズ情報からヘッダのサイズとサイズ調整フィールドの値を差し引くことで求められます。チャンクは常にワード境界から始まるため、チャンクの実際のサイズは常に2の倍数になります。

サイズフィールド(3バイト)

この3バイト値は、チャンクヘッダ自身のサイズを含む、アプリケーションから要求されたサイズよりも大きなチャンクサイズを格納しています。チャンクの最大データサイズは、64 KB 弱です。

Lock:owner バイト

サイズ情報の次のバイトは、上位ニブルにロックカウント、下位ニブルにオーナーIDを格納するバイトです。ロックカウントはチャンクがロックされるたびにインクリメントされ、アンロックされるたびにデクリメントされます。移動可能なチャンクは、アンロックせずに最大で 14 回ロックできます。移動不可能なチャンクでは、ロックフィールドには常に 15 が格納されています。オーナーIDはメモリチャンクの所有者を決めるためのもので、アロケート時にメモリマネージャによって設定されます。オーナーID情報はデバッグやアプリケーションが異常終了した場合のガベージコレクションに有用です。

hOffset フィールド(3バイト)

チャンクヘッダの最後の3バイトには、そのチャンクのマスタポインタからチャンクヘッダまでの距離を2で割った値が設定されます。マスタポインタテーブルがチャンク自身よりも大きなアドレスにある場合、このオフセットが負の値になり得ることに注意して下さい。移動不可能なチャンクはマスタポインタテーブルにエントリが必要ないため、このフィールドは0になります。

 

ローカル ID の構造

IMPORTANT

ローカルIDの構造は将来変更される可能性があります。チャンクを操作する場合はAPIを使用して下さい。

データベースレコードやその他のデータベース情報を含むチャンクは、データマネージャによってローカルID経由で管理されています。ローカルIDはカードに関連付けられており、カードがどのメモリスロットに入っていても有効です。ローカル ID はカードのベースアドレスがわかってしまえば簡単にポインタあるいはハンドルに変換することができます。

ローカル ID の上位 31 ビットは、カードの先頭からチャンクまたはマスタポインタまでのオフセットです。最下位ビットは、それがハンドルのローカルIDであればセットされ、ポインタのローカルIDであればクリアされます。

MemLocalIDToGlobal 関数は、ローカル ID とカード番号( 0 か 1 のいずれか )をポインタまたはハンドルに変換します。この関数はローカルIDをそのカード上のポインタまたはハンドルに変換するために、カード番号を見てカードのベースアドレスを加算します。

 

メモリマネージャの使用

ダイナミックヒープに(動的確保、スタック、大域変数などのための)メモリをアロケートする場合はメモリマネージャAPIを使用し、ストレージヒープに(ユーザーデータ用の)メモリをアロケートする場合はデータマネージャAPIを使用します。データマネージャは低水準のアロケーションを実行するために必要に応じてメモリマネージャをコールします(詳細は 「6-1 データマネージャ」を参照して下さい)。

 

メモリマネージャAPIの概要

移動可能なチャンクをアロケートする場合、MemHandleNew をコールして必要なチャンクのサイズを渡します。このチャンクのデータを読み書きする前に MemHandleLock をコールし、ハンドルをロックしてポインタを取得する必要があります。チャンクをロックするたびにロックカウントがインクリメントされます。エラーが返されるまでに、最大で14回チャンクをロックできます(移動不可能なチャンクのロックフィールドは15固定になっていることを思い出して下さい)。MemHandleUnlock は MemHandleLock と逆の効果を持ちます。つまり、ロックフィールドの値から1を減じます。ロックカウントが 0 まで減らされると、チャンクはメモリマネージャによって移動することが可能になります。

アプリケーションがダイナミックヒープからメモリをアロケートする場合、メモリマネージャはオーナー ID をアプリケーションと関連付けるために使用します。システムはさらに、オーナー ID 情報のスペシャルビットをセットすることで、そのチャンクが現在アクティブなアロケーションかどうかを識別します。アプリケーションが終了すると、ダイナミックヒープ内でこのビットがセットされた全てのチャンクは自動的に開放されます。

アプリケーションが終了しても破棄されないチャンクをシステムがアロケートしたい場合、システムは MemHandleSetOwner 関数または MemPtrSetOwner 関数をコールしてオーナー ID に 0 をセットします。特殊な状況を除き、これらの関数は通常アプリケーションからは使用しません。例えば、カレントアプリケーションが SysUIAppSwitch で起動する新しいアプリケーションにパラメータブロックを渡す場合、そのブロックのオーナーはシステムになっていなければなりません。さもないと、カレントアプリケーションが終了する際、システムがそのアプリケーションのアロケートしたメモリを開放した時点でブロックは削除されてしまいます。

IMPORTANT

MemPtrSetOwner() または MemHandleSetOwner() を呼び出してメモリチャンクの所有者をシステムに変更したら、それを自分で開放しないで下さい。オペレーティングシステムは SysUIAppSwitch() または SysAppLaunch() で起動されたアプリケーションが終了すると、そのチャンクを開放します。

移動可能なチャンクのサイズを取得するには、そのハンドルを MemHandleSize 関数に渡します。サイズを変更するには、MemHandleResize 関数をコールします。チャンクのすぐ後ろに空き領域がない限り、一般的にロックされたチャンクのサイズを増やすことはできません。チャンクがロックされていない場合、メモリマネージャはサイズを増やすためにヒープを別の場所に移動することができます。チャンクが不要になったら、MemHandleFree をコールします。この関数はチャンクがロックされていても開放します。

ロックされている移動可能なチャンクのポインタがあれば、MemPtrRecoverHandle をコールすることでそのハンドルを復元することができます。実際には、MemPtrSize 関数を含む全ての MemPtr〜 関数はロックされた移動可能なチャンクのポインタを操作しています。

移動不可能なチャンクをアロケートする場合、MemPtrNew をコールして必要なサイズを渡します。この関数は直接読み書きが可能なチャンクのポインタを返します。

NOTE

サイズが 0 バイトのチャンクをアロケートすることはできません。

移動不可能なチャンクのサイズを取得するには、MemPtrSize 関数をコールします。サイズを変更するには、MemPtrResize をコールします。チャンクのすぐ後ろに空き領域がない限り、一般的に移動不可能なチャンクのサイズを増やすことはできません。チャンクが不要になったら、MemPtrFree をコールします。この関数はチャンクがロックされていても開放します。

メモリをある場所から他へ移動させたり、メモリを特定の値で塗り潰したい場合、メモリマネージャのユーティリティ関数である MemMove や MemSet を使用します。

ほとんどの場合、メモリを開放する正しい方法は MemPtrFree 関数か MemHandleFree 関数をコールすることです。

NOTE

Palm ハンドヘルドにおける正しいメモリ使用に関する重要な注意事項と実用的なアドバイスについては、「1 小箱の中の Palm OS プログラミング」の"頑丈なコードを書くこと"を参照してください。

 

ストレージヒープのサイズとメモリ管理の仕組み

Palm OS のバージョン 1.0 では、個別のストレージヒープの上限サイズは 64KB に制限され、メモリマネージャがストレージヒープ間でオブジェクトを自動的に移動して、ストレージヒープのいずれかが満杯にならないようにしていました。この方式は大きなオブジェクトに使用できるような連続領域を減少させてしまうという傾向がありました。バージョン 2.0 のメモリマネージャはこのアプローチを捨て、ヒープの連続領域を維持するようになりましたが、個々のヒープの最大サイズはまだ 64 KB に制限されていました。Palm OS バージョン 3.0 ではヒープの 64 KB サイズ制限が廃止され、2つのヒープが作成されるようになりました。1つは 96KB のダイナミックヒープで、もう1つはカードの残り RAM サイズに等しいストレージヒープです。

Palm OS 3.5 からは、ダイナミックヒープのサイズは以下に示すように、システムが利用可能なメモリ量を基準として決められるようになりました。

デバイスのメモリサイズ ヒープのサイズ
2 MB 未満 64 KB
2 MB 以上 128 KB
4 MB 以上 256 KB

 

パフォーマンスの最適化

Palmハンドヘルドはヒープ領域やストレージに制限があるため、最適化が非常に重要になります。アプリケーションをできるだけ速く効率的にするために、ヒープの使用を第一に最適化し、2番目に速度を、最後にコードサイズを最適化します。

メモリの使用を最適化するために、以下のガイドラインに従って下さい。

 

 

 

← 2 節に戻る ↑5 章トップへ 4 節に進む →

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.