【メインフレーム技術深掘り】PL/IのAUTOMATIC変数が秘める「スタック」の真実と再帰の罠
現場の諸君、今日もメインフレームの保守お疲れ様。
最近の若いエンジニアを見ていると、PL/Iのスタックメモリ管理、特に`AUTOMATIC`属性の挙動を軽く見て痛い目を見るケースが後を絶たない。
「なぜかバッチが異常終了する」「VSAMの読み込み中に突如としてS0C4やS80Aが発生する」。その原因の多くは、この`AUTOMATIC`変数のライフサイクルと、メインフレーム特有のスタック領域に対する理解不足にあるんだ。
今日は、PL/Iの基本構造である`PROCEDURE`と、その背後で動くメモリ管理の「リアル」について語ろうと思う。
—
1. AUTOMATIC属性:PROCEDUREの呼吸と共に生きる変数
PL/Iにおいて、明示的に属性を指定しない場合、デフォルトで割り当てられるのが`AUTOMATIC`だ。これは、`PROCEDURE`が呼び出された瞬間にスタック領域へ変数を確保し、`END`ステートメントで処理が戻る際に解放される。
一見シンプルだが、ここに「見えないコスト」がある。
/i
/ 大規模バッチ処理のサンプル構造 /
MAIN_PROC: PROC OPTIONS(MAIN);
/ VSAM制御用ブロック /
DCL VSAM_REC CHAR(4096) AUTOMATIC; / 4KBの自動変数 /
/ 処理開始 /
CALL SUB_PROCESS;
END MAIN_PROC;
SUB_PROCESS: PROC;
/ ここでスタックが消費される /
DCL BUFFER CHAR(32767) AUTOMATIC; / 32KBのバッファ /
/ … 処理 … /
END SUB_PROCESS;
重要なのは、`SUB_PROCESS`が呼び出されるたびに、この32KBがスタックの頭上に積み上げられるということだ。もし、このプロシージャの中でさらに別のルーチンを呼び出し、それが再帰構造になっていたらどうなるか?
2. 再帰呼び出しとスタックオーバーフローの恐怖
メインフレームのスタック領域は有限だ。特にバッチジョブの領域(REGION)が不足している場合、再帰呼び出し(Recursive Call)はまさに「爆弾」となる。
再帰の深さが100回になれば、スタック領域は単純計算で `32KB × 100 = 3.2MB` 圧迫される。これに加え、`ON`ユニットの制御情報や、OSが管理するレジスタセーブエリアが積み重なる。
現場でよくある失敗は、「巨大な配列をAUTOMATICで宣言したまま、再帰関数を回す」ことだ。これをやると、あっという間にスタックオーバーフロー(S80AやS0C4)の引き金になる。
対策の知恵:
どうしても巨大なメモリが必要な場合は、`CONTROLLED`属性を使って動的に領域を確保するか、あるいはメインメモリが許す限り`STATIC`属性への移行を検討すべきだ。ただし、`STATIC`はスレッドセーフではないので、マルチタスクや再帰が絡む場合は注意が必要だぞ。
3. 実践:VSAMアクセスとONユニットの組み合わせ
実務では、VSAMの読み込みとスタック管理を組み合わせて書くことが多い。以下に、現場の標準的な作法を記す。
/i
GET_DATA_PROC: PROC;
DCL VSAM_BUF CHAR(1024) AUTOMATIC;
DCL EOF_FLAG BIT(1) INIT(‘0’B);
/ ONユニットによる例外制御 /
ON ENDFILE(VSAM_FILE) BEGIN;
EOF_FLAG = ‘1’B;
END;
DO WHILE(^EOF_FLAG);
READ FILE(VSAM_FILE) INTO(VSAM_BUF);
IF ^EOF_FLAG THEN DO;
/ BUILTIN関数によるデータ検証 /
IF VERIFY(SUBSTR(VSAM_BUF, 1, 10), ‘0123456789’) ^= 0 THEN DO;
PUT SKIP LIST(‘データ不正検知:’, VSAM_BUF);
END;
END;
END;
END GET_DATA_PROC;
ここで意識してほしいのは、`ON`ユニットが確立されるたびに、コンパイラはスタック上にその制御情報を配置するということだ。`DO`ループの中で頻繁に`ON`を書き換えるような設計は避け、できるだけプロシージャの入り口付近で定義を完結させるのが、安定したコードを書くための秘訣だ。
—
ベテランからのアドバイス:デバッグの心構え
もし君が担当しているプログラムでスタック関連の異常が起きたら、まずは以下の3点を確認すること。
1. REGION指定の確認: JCLのREGIONパラメータは妥当か?(最近は`0M`指定で逃げるケースも多いが、まずは設計の見直しが先だ)
2. 変数のサイズ: 巨大な`AUTOMATIC`変数は存在しないか?
3. 再帰の深さ: ループ条件にバグはないか?再帰が無限ループに陥っていないか?
PL/Iは非常に強力な言語だ。スタックという「メモリの呼吸」を理解できれば、君はもうどんなレガシーなコードでも恐れることはない。
次回のブログでは、`CONTROLLED`変数を用いた効率的なメモリ管理と、ポインタ制御(ADDRやOFFSET)による高速化手法について深く掘り下げていこうと思う。
質問があればコメント欄か、社内の内線で聞いてくれ。現場からは以上だ。
