【入門編】AUTOMATIC属性とSTATIC属性のメモリ配置の違い – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のプログラミング言語の経験がある方にとって、IBMメインフレームの「PL/I(ピーエルワン)」という名前を聞くだけで、なんだか分厚いマニュアルと古い呪文のようなコードを想像して身構えてしまうかもしれませんよね。

「なんだか難しそう…」
「メモリ管理とか、レガシー特有の怖いルールがあるんじゃないの?」

そんな風に不安を感じている方も多いと思いますが、どうか安心してください。一見すると独特なPL/Iの文法も、裏側にある仕組みや「メモリの居場所」という視点から一つずつ紐解いていけば、JavaやCOBOLで見慣れた概念と驚くほどすんなり繋がります。

今回は、PL/Iのデータ制御における基本中の基本でありながら、バッチプログラムのパフォーマンスや思わぬバグ(初期化漏れなど)に直結する「AUTOMATIC属性とSTATIC属性のメモリ配置の違い」について、優しく丁寧に解説していきますね。

1. 変数の「居場所」を意識していますか?

Javaならメソッドの中で宣言したローカル変数はメソッド終了と共に消え、クラスのメンバ変数はインスタンスが存在する間は残りますよね。COBOLなら `WORKING-STORAGE SECTION` に書いたデータはプログラム実行中ずっと存在し、`LOCAL-STORAGE SECTION` に書いたデータは呼び出しごとに新しく作られます。

PL/Iでも、これらと全く同じことが起きています。それを決めているのが、変数に指定するストレージ・クラス属性(Storage Class Attributes)である `AUTOMATIC``STATIC` です。

まずは、この2つがメモリ上のどこに住んでいて、どんなライフサイクル(生存期間)を送っているのか、イメージしやすい例えで覗いてみましょう。

AUTOMATIC = ホテルの「一時滞在ルーム」

`AUTOMATIC`(省略したときのデフォルトもこれです!)は、いわば「ホテルのチェックインからチェックアウトまでの一時的な部屋」です。

  • いつ作られる?:その変数が宣言されたブロック(PROCEDUREやBEGINブロック)に入った瞬間、スタック領域にパッと部屋が用意されます。
  • いつ消える?:そのブロックから外に出た瞬間、部屋ごとキレイに片付けられて(メモリが解放されて)消滅します。
  • 値の保持は?:ブロックを抜けるたびに部屋がリセットされるので、次に呼ばれたときには前のデータは残っていません。毎回、初期化(あるいは不定値からのスタート)になります。

STATIC = マンションの「固定の自室」

一方の `STATIC` は、「プログラムが起動している間、ずっと自分専用としてキープされているマンションの一室」です。

  • いつ作られる?:プログラム(ロードモジュール)がメモリにロードされた瞬間、静的領域にドンと陣取ります。
  • いつ消える?:プログラムが完全に終了してメインフレームからアンロードされるまで、ずっとそこに居続けます。
  • 値の保持は?:一度代入した値は、次にそのプログラム(あるいは内部のサブルーチン)が呼ばれてもそのまま保持され続けます

2. 実際のPL/Iコードで挙動の違いを見てみましょう

百聞は一見に如かず。実際にPL/Iで書かれたサンプルプログラムを見てみましょう。「大文字ベース」というレガシーの伝統にならい、すべて大文字で記述しています。

1
MYSAMP: PROC OPTIONS(MAIN);

/ ————————————————– /
/ メイン処理:サブプロシージャを2回呼び出して挙動を確認する /
/ ————————————————– /
PUT SKIP LIST(‘— 1回目の呼び出し —‘);
CALL CHECK_STORAGE;

PUT SKIP LIST(‘— 2回目の呼び出し —‘);
CALL CHECK_STORAGE;

RETURN;

CHECK_STORAGE: PROC;

/ STATIC属性:ロードモジュール内に静的に配置され、値を保持する /
DCL W_STATIC_CNT FIXED BIN(31) STATIC INIT(0);

/ AUTOMATIC属性(省略可能):呼び出し毎にスタックに作られ、毎回初期化される /
DCL W_AUTO_CNT FIXED BIN(31) AUTOMATIC INIT(0);

/ それぞれのカウンタをインクリメント /
W_STATIC_CNT = W_STATIC_CNT + 1;
W_AUTO_CNT = W_AUTO_CNT + 1;

/ 結果を出力 /
PUT SKIP EDIT (‘STATICカウンタ = ‘, W_STATIC_CNT) (A, F(5));
PUT SKIP EDIT (‘AUTOMATICカウンタ = ‘, W_AUTO_CNT) (A, F(5));

END CHECK_STORAGE;

END MYSAMP;

このプログラムの実行結果はどうなる?

JavaやCOBOLの感覚で、「メソッドが呼ばれるたびに変数は0からスタートするはずだ」と思っていると、この実行結果で少しドキッとするかもしれません。

— 1回目の呼び出し —
STATICカウンタ = 1
AUTOMATICカウンタ = 1
— 2回目の呼び出し —
STATICカウンタ = 2
AUTOMATICカウンタ = 1

おや? `STATIC` の方は、2回目の呼び出しで `2` に増えていますね。
これは、`CHECK_STORAGE` プロシージャを抜けても、`W_STATIC_CNT` が住んでいる「マンションの部屋」がそのまま残っていて、値が保持されていたからです。

一方、`AUTOMATIC` の方は、1回目も2回目も `1` のままです。
こちらはプロシージャを抜けた瞬間に部屋ごと綺麗さっぱり消え、2回目に呼ばれたときには「新築のまっさらな部屋」が新しく用意されたからですね。

3. 実務の現場(マイグレーションや保守)で直面する「罠」と注意点

「なるほど、じゃあ何でもかんでも `STATIC` にしておけば値が残って便利じゃないか!」と思ったそこのあなた…ちょっと待ってください。ここにレガシーシステムの恐ろしい罠が潜んでいます。

バッチ処理における「再入可能性(リエンタラビリティ)」の崩壊

メインフレームのオンラインシステムや、マルチタスクで同時に何件も動くバッチプログラムでは、同じプログラムが複数のタスクから同時に(あるいは連続して)呼び出されます。

もし、あるべきではないデータ(作業用のワークエリアなど)に `STATIC` を使ってしまうと、「Aというユーザーが使ったときのデータが、次に動いたBユーザーの処理に悪影響を及ぼす(データの汚染)」という、原因究明が非常に困難な不具合(バグ)を引き起こします。

  • 基本のルール
  • 計算結果を次の呼び出しへ引き継ぎたい場合(累積集計など)を除き、基本はすべて `AUTOMATIC`(またはデフォルト)にする。
  • 定数や、プログラム全体で共有すべき参照用データには `STATIC` を使う。

初期化(INIT)の挙動の罠

もう一つ気をつけたいのが、`INIT` 句(初期値)の挙動です。

  • `STATIC INIT(10)` は、プログラムがロードされたときに一度だけ `10` がセットされます。2回目以降の呼び出しでは `INIT` はスキップされ、前回変更された値が維持されます。
  • `AUTOMATIC INIT(10)` は、そのブロックに突入するたびに、スタックにメモリが確保されると同時に毎回 `10` がセットされます。

「前回保持していたはずのデータが消えている!」というトラブルの多くは、この `AUTOMATIC` と `STATIC` の特性、そして `INIT` 句の思い違いから発生しています。

まとめ

今回は、PL/Iのデータ制御における `AUTOMATIC` と `STATIC` のメモリ配置と生存期間について解説しました。

  • `AUTOMATIC`:スタック領域。ブロックに入るたびに生まれ、抜けたら消える。毎回リセットされる。
  • `STATIC`:静的領域。プログラム起動から終了までずっと残り、値も保持され続ける。

「PL/Iのストレージ属性って、要するにデータのライフサイクルの管理なんだな」とイメージできたのではないでしょうか。一見古めかしい言葉の裏側には、コンピュータのメモリ構造に直結した非常に合理的でシンプルなルールが隠されています。

レガシーシステムのソースコードを読むときや改修するとき、変数の宣言部に `STATIC` や `AUTOMATIC`(あるいは何も書かれていないデフォルト)を見つけたら、「お前は一時的な部屋に住んでいるのか、それとも自分のマンションを持っているのかい?」と心の中で話しかけてみてください。きっと、コードの意図がスルスルと見えてくるはずです。

それでは、次回のPL/I深掘り解説もお楽しみに!

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