【実務・中級編】CICSにおけるEXEC CICS GETMAIN/FREEMAINによる動的メモリ管理 – PL/Iの基本構文とデータ制御実践ガイド

おい、そこの君。ちょっと手を止めてこっちを向いてくれ。
今、オンライン画面から送信された電文の処理中に、CICS領域が突如として `AKMT` や `ASRA` の嵐に見舞われ、最終的にオートループやストレージ不足(SOS)で領域ごとごっそりダウンした――そんな悪夢のようなコールを受けたところか?

……フッ、顔色を見れば大体わかる。間違いない、ストレージリーク(メモリの解放漏れ)だ。

我々が日々向き合っているIBMメインフレームの基幹システムにおいて、CICS上のPL/Iオンラインプログラムは、いわば高速道路をノーブレーキで走り抜ける超重量級のトレーラーのようなものだ。ひとたび動的メモリ管理を誤れば、システム全体を巻き込む大惨事につながる。

今回は、CICS環境下における `EXEC CICS GETMAIN` と `EXEC CICS FREEMAIN` の正しい作法、そしてPL/I特有のデータ制御と識別子の特性を絡めながら、現場のエンジニアが知っておくべき「生き残りの知見」を徹底的に叩き込んでやろう。心して聞くように。

—

1. PL/Iの識別子規則とCICS動的メモリ管理の基本

まず大前提として、PL/IにはCやCOBOLのような「言語独自の予約語(RESERVED WORDS)」という概念がほぼ存在しない。極端な話、`IF` や `DO` といった制御構文すら、文脈によっては変数名(識別子)として定義できてしまう言語だ。この柔軟性は諸刃の剣であり、CICSのAPI群を組み込む際には、コーディング標準を厳格に設けておかないと、コンパイラは通れども実行時にとんでもないバグを生む温床となる。

さて、CICSオンラインプログラム(AMODE(31)が主流となった現代においても)では、タスクの寿命(Task Lifetime)に応じた動的ストレージの確保が必須だ。
ワーキングストレージ(`DCL` で静的に定義された領域)だけでは、可変長の大量データや、サブタスク間で受け渡す巨大なレコード領域を保持しきれない。ここで登場するのが、お馴染みの `EXEC CICS GETMAIN` である。

ストレージリークが発生するメカニズム

なぜストレージリークが起きるのか? 原因は単純明快だ。
「確保した(GETした)はいいが、解放する(FREEする)前に処理がリターンしている、あるいはエラー系(ABEND)のハンドリングでバイパスされている」 これに尽きる。

CICSでは、タスクが正常終了(`EXEC CICS RETURN` など)すれば、そのタスクが取得したストレージは基本的にはCICSによって自動解放される。しかし、以下の悪条件が重なると、タスクが存続している間、あるいはタスク終了後すらストレージが宙に浮き、DSA(Dynamic Storage Area)をじわじわと侵食していく。

1. ループ処理の中で毎回 `GETMAIN` を叩いているのに、対応する `FREEMAIN` がループ外にある、あるいは条件分岐のせいでスキップされている。
2. `ONPLIB` や `ON-unit`(例外条件処理)の中で予期せぬエラーを捕捉し、本来通るべき `FREEMAIN` を通らずに `GO TO` や `ABEND` で抜けている。
3. ポインタ変数(`POINTER`)の値を別の値で上書きしてしまい、元のアドレスを見失ってしまい、二度と `FREEMAIN` できなくなった(これが一番タチが悪い)。

—

2. 実践:安全確実なGETMAIN/FREEMAIN実装パターン

百聞は一見に如かずだ。実際の現場でそのまま使える、堅牢なPL/Iソースコードの構造を見てみよう。
今回は、VSAMファイルから可変長レコードを動的に読み込み、ワークエリアを動的確保して処理するオンラインサブルーチンのイメージだ。

1
/ ================================================================= /
/ プログラムID: MEMO01PL /
/ 概要 : CICS動的ストレージ管理サンプル /
/ ================================================================= /
MEMO01PL: PROC OPTIONS(MAIN, CICS);

/ — 1. 変数・ポインタの宣言 — /
DCL WS_EIBTRMID CHAR(4); 端末ID保存用
DCL WS_RESPCODE FIXED BIN(32); CICS応答コード
DCL WS_LENGTH FIXED BIN(32) INIT(4096); 要求サイズ (4KB)

/ 動的領域を指し示すポインタ変数 /
DCL MEM_PTR POINTER INIT(NULL());

/ 取得するストレージのベースとなる基底構造体 /
DCL 1 WORK_AREA BASED(MEM_PTR),
3 REC_HEADER CHAR(8), レコードヘッダ
3 REC_BODY CHAR(4088); データ本体

/ — 2. 処理開始 — /
WS_EIBTRMID = EIBTRMID;

/ 動的メモリの確保 (GETMAIN) /
EXEC CICS GETMAIN
SET(MEM_PTR)
LENGTH(WS_LENGTH)
INITIMAGE(X’00’) ; 領域をゼロクリアして安全確保

/ 確保失敗時のチェック /
IF EIBRESP <> DFHRESP(NORMAL) THEN DO;
/ 異常終了処理へ /
GO TO ERROR_ROUTINE;
END;

/ — 3. メイン処理(動的領域の利用) — /
/ 識別子のスコープとポインタ操作に十分注意する /
WORK_AREA.REC_HEADER = ‘HDR001’;
WORK_AREA.REC_BODY = ‘これは動的に確保された領域のテストデータです。’;

/ 擬似的なビジネスロジックの呼び出し /
CALL PROCESS_SUB(MEM_PTR);

/ — 4. 正常な解放処理 (FREEMAIN) — /
/ 必ず確保したポインタを指定して解放する /
IF MEM_PTR ^= NULL() THEN DO;
EXEC CICS FREEMAIN
DATA(MEM_PTR);

/ 二重解放(ダブルフリー)や誤操作を防ぐためポインタをクリア /
MEM_PTR = NULL();
END;

EXEC CICS RETURN;

RETURN;

/ — 異常処理ルーチン — /
ERROR_ROUTINE:
/ エラー時でもストレージが残っていれば確実に解放する防衛的プログラミング /
IF MEM_PTR ^= NULL() THEN DO;
EXEC CICS FREEMAIN
DATA(MEM_PTR);
MEM_PTR = NULL();
END;

EXEC CICS ABEND ABCODE(‘MEM1′);

END MEMO01PL;

このコードを見て、「おっ」と気づいてほしいポイントがいくつかある。
まず、`INITIMAGE(X’00’)` だ。`GETMAIN` 時にメモリをゼロクリアさせることで、初期値不定による思わぬ暴走を防いでいる。
そして何より重要なのが、正常系・異常系(`ERROR_ROUTINE`)の双方で `MEM_PTR` のヌルチェックを行い、確実に `FREEMAIN` を実行している点だ。さらに、解放直後に `MEM_PTR = NULL();` とポインタを無効化(クリア)している。これを徹底するだけで、ストレージリークの発生確率は劇的に下がる。

—

3. 現場で使える!ストレージリークのデバッグ手法

もし、本番稼働中のCICS領域で「ストレージリークが疑われる」というフェーズに入ってしまったら、君はどう動く?
慌ててソースコードの全行を目視チェックするのは素人のやることだ。プロのメインフレームエンジニアは、ツールとダンプを使いこなして秒速で特定する。

①CEDF(Execution Diagnostic Facility)の活用

まずは該当トランザクションを `CEDF` モードでインタラクティブに実行する。
`GETMAIN` が発行された瞬間の `SET` アドレス(ポインタの値)を控え、処理が進むにつれてそのアドレスのストレージがどう扱われているかを追跡する。もし、画面遷移やループの周回ごとに `GETMAIN` のアドレスが変わり、古いアドレスが解放されないまま新しいアドレスがアロケートされていれば、その瞬間にリーク確定だ。

②CECIでのストレージ状況確認

CICSのトランザクション `CECI` を使い、`INQUIRE DSAS` などを発行して、どのDSA(CDSA、UDSA、EDSAなど)が圧迫されているかをリアルタイムで監視する。

③CICSストレージダンプ(S0C4やAICAなどの解析)

最悪の場合、タスクが異常終了した際のストレージダンプ(SDUMP)をIPCS(Interactive Problem Control System)で解析することになる。

  • ダンプ内から問題のトランザクションのTWA(Transaction Work Area)やタスク関連制御ブロックを辿る。
  • 取得されたが解放されていないセル(Storage Accounting Area: SAA を持つブロック)のチェーンを追跡し、どのプログラムのどのオフセットで `GETMAIN` されたものかを突き止める。

—

4. シニアアーキテクトからの教訓

最後に、長年レガシーシステムを渡り歩いてきた私から、君たち後輩エンジニアへアドバイスを送ろう。

PL/Iは非常に表現力が高く、ハードウェアに近い低水準なポインタ操作から、高度な構造体制御まで思いのままに記述できる素晴らしい言語だ。しかし、それは「プログラマが意図した通りに動く」と同時に、「プログラマがやらかしたミスも、そのまま忠実に実行してしまう」という残酷な裏返しでもある。

CICSの動的メモリ管理において、`GETMAIN` を書いたら、その数行下(あるいは確実に通るエラーハンドリング内)に必ずセットで `FREEMAIN` を置く。ポインタを使い回さない。この「当たり前の規律」を面倒くさがらずにコードに落とし込むこと。

綺麗な設計、そして美しい防衛的コーディングこそが、深夜の緊急呼び出しを防ぐ唯一の防壁だ。
さあ、コーヒーでも飲んで、もう一度自分の担当プログラムの `GETMAIN` を見直してみようか。健闘を祈る。

タイトルとURLをコピーしました