1 概要
本ドキュメントではnAG Fortranコンパイラを利用して、良く見られるメモリの問題をデバッグする方法を説明します。Windows®では、これらの機能はnAG Fortran Builder(5.2以降)に含まれています。
2 初期化されていないポインタ
初期化されていないポインタとは指示先(例 ALLOCATEもしくはポインタ代入(=>)によって) と結合されていないだけでなく空値(null)化されていない(例 NULLIFYによって)ものを示します。
もしこのようなポインタが使われた場合の動作は未定義です。(でたらめなメモリを示すかもしれませんし、空値(null)であるかもしれませんし、無効なアドレスであるかもしれません。) このような場合プログラムがクラッシュしてしまったり思わぬ結果が返されたりします。
初期化されていないポインタの検出をnAG Fortranコンパイラで有効するためには -C=pointerオプションを使用します。 このオプションはnullポインタを利用しようとした場合に(プログラムがクラッシュしてしまうことなく)それとわかるエラーメッセージを出力します。
3 ぶらさがった(dangling)ポインタ
3.1 “ぶらさがった(dangling)ポインタ”とは?
ぶらさがったポインタとは既に存在しない指示先を参照するポインタを指します。指示先は以下の2つの方法で存在しなくなります:
- 指示先が手続き内の非保存局所変数であった際に手続きから復帰した場合。
- 指示先が割付けられた実体であり、それが解放された場合。
Fortranの用語では、このように指示先が存在しなくなった状態をポインタが“不定”であると表現します。
CおよびC++プログラムにおいてもこの2つはよく発生し、分かりにくいエラーやクラッシュの原因となります。 非常に小さなプログラムを除きこのような状況を見つけ出すことは コンパイラの助け無くしては困難です。
valgrind等のツールでもぶらさがったポインタを検出することが可能ですが、既に一度利用されたメモリ領域(例えば他の手続きで利用済みのメモリ領域を別の手続きで利用する場合や、同じような大きさの変数で新たにメモリを割り付けたような場合等)でこれを検出することができません。
3.2 -C=danglingオプション
nAG Fortranコンパイラでは-C=danglingオプションを指定することによりぶらさがったポインタの検出が可能です。 このオプションを指定すると-C=pointerも併せて指定されたことになります。 このオプションを指定してコンパイルされた手続きとそうでない手続きを混在させることも可能ですが、その場合にはこのオプション付きでコンパイルされた手続きのみが診断対象となります。3.3 Dangling example 1: Return
Program dangle1 Real,Pointer :: x(:,:) Call make_dangle(x) Call use_dangle(x) Contains Subroutine make_dangle(p) Real,Pointer :: p(:,:) Real,Target :: y(100,200) p => y End Subroutine Subroutine use_dangle(p) Real,Target :: z(100,200) Real,Pointer :: p(:,:) p(10,10) = 0 p => z End Subroutine End“dangle1.f90”が上記を含む場合で -C=danglingオプションを指定したコンパイルされていた場合、 以下のようなエラーが検出されます。
英語の場合: Runtime Error: dangle1.f90, line 14: Reference to dangling pointer P Target was RETURNed from procedure DANGLE1:MAKE_DANGLE Program terminated by fatal error 日本語の場合: 実行時エラー: dangle1.f95, line 14: ぶら下がった(Dangling)ポインタPへの参照が行われました 手続きDANGLE1:MAKE_DANGLEから指示先が返されました 致命的なエラーでプログラムが終了しました
この例は簡単なメモリ診断ツールでは検出できないものです。 これはこの例でYによって使用済のメモリ領域がZで再利用され、簡単なメモリ診断ツールでは、利用可能な領域が再利用されたと誤って判断されてしまうためです。
3.4 Dangling example 2: Deallocate
以下はもう一つのタイプのぶらさがったポインタの例(領域が解放されたケース)です。Program dangle2 Real,Pointer :: x(:),y(:),z(:) Allocate(x(100)) y => x Deallocate(x) Allocate(z(100)) y = 3 Deallocate(z) End“dangle2.f90”が上記を含む場合で -C=danglingオプションを指定したコンパイルされていた場合、 以下のようなエラーが検出されます。
英語の場合: Runtime Error: dangle2.f90, line 7: Reference to dangling pointer Y Target was DEALLOCATEd at line 5 of dangle2.f90 Program terminated by fatal error 日本語の場合: 実行時エラー: dangle2.f95, line 7: ぶら下がった(Dangling)ポインタYへの参照が行われました 指示先がmain.f95の5行目で解放されています 致命的なエラーでプログラムが終了しました
割付け可能(ALLOCATABLE)な指示先も自動後始末もしくは自動再割付けにより解放されます。このような場合も適切なエラーメッセージが出力されます。例)
英語の場合: Runtime Error: dangle3.f90, line 7: Reference to dangling pointer X Target was deallocated by automatic reallocation at line 6 of dangle3.f90 日本語の場合: 実行時エラー: dangle3.f95, line 7: ぶら下がった(Dangling)ポインタXへの参照が行われました 指示先はmain.f95の6行目で自動再割付けによって解放されました 致命的なエラーでプログラムが終了しました
4 メモリリーク
“メモリリーク”もプログラムのおかしな動作を誘発するメモリ問題の一つです。 メモリリークは多くの場合、メモリ領域が割付けられたものの、領域の解放が行われずにその領域をを指し示すポインタが再利用されたかもしくは存在しなくなった場合に見られます。メモリリークの発生は“自動ガーベジコレクション”により完全になくすことができます。 nAG Fortranコンパイラでこれを行うには-gcオプションを指定します。自動ガーベジコレクションは、割付けられた実体を示すポインタが無くなった時点で自動的に領域の解放を行います。
メモリリークはnAG Fortranコンパイラの -mtrace オプションを指定してメモリ割付けトレースツールを有効にすることと、そこからの出力を nagfmcheck ツールで更に解析することにより検出することが可能です。
4.1 Memory leak example
Program memory_leak Real,Pointer :: x(:,:) Allocate(x(10,20)) ! Leak x = 0 Allocate(x(3,4)) Deallocate(x) Allocate(x(5,6)) ! Leak Allocate(x(20,30)) x = 3 Deallocate(x) End“memleak.f90”が上記を含み-mtrace=allオプションを指定してコンパイルされている場合に、以下のようなメモリ割付けトレース情報を出力します。
[Allocated item 1 (size 1025) = Z'2E0008'] [Allocated item 2 (size 1025) = Z'2E0418'] [Allocated item 3 (size 1025) = Z'2E0828'] [Allocated item 4 (size 800) at line 3 of memleak.f90 = Z'2F0008'] [Allocated item 5 (size 48) at line 5 of memleak.f90 = Z'2F0330'] [Deallocated item 5 (size 48, at Z'2F0330') at line 6 of memleak.f90] [Allocated item 6 (size 120) at line 7 of memleak.f90 = Z'2F0368'] [Allocated item 7 (size 2400) at line 8 of memleak.f90 = Z'2F03E8'] [Deallocated item 7 (size 2400, at Z'2F03E8') at line 10 of memleak.f90] [Deallocated item 2 (size 1025, at Z'2E0418')] [Deallocated item 3 (size 1025, at Z'2E0828')] [Deallocated item 1 (size 1025, at Z'2E0008')]この結果を付属のnagfmcheckツールを用いて更に解析すると、 以下のような結果が得られます。
7 allocations ***MEMORY LEAK: LEAK: Allocation 4 (size 800) = Z'2F0008' at line 3 of memleak.f90 LEAK: Allocation 6 (size 120) = Z'2F0368' at line 7 of memleak.f90
これらはプログラム全体を通した場合でのみ有効な検出方法ですが役立ちます。
4.2 メモリトレースの他の活用方法
メモリトレース出力には、自動配列、可変長文字列、配列仮領域、コンパイラ実行時ライブラリからの内部利用領域を含むすべてのメモリ割付けと解放が含まれます。 これによりユーザプログラムが期待以上に大きな領域を必要とする理由を調べることが可能です。メモリトレース出力は非常に大きくなる可能性があるので、出力先(ファイル名)を環境変数nAGFORTRAN_MTRACE_FILEに指定することが可能です。(指定をしないと標準エラー出力が出力先です)