【Oracle】ORA-04031: 共有メモリを割り当てられません 完全ガイド|共有プール断片化・V$SGASTAT調査・DBMS_SHARED_POOL.KEEP・予防策まで解説

【Oracle】ORA-04031: 共有メモリを割り当てられません 完全ガイド|共有プール断片化・V$SGASTAT調査・DBMS_SHARED_POOL.KEEP・予防策まで解説 Oracle

ORA-04031: 共有メモリのNバイトを"共有プール","ヒープ名","タイプ","サブタイプ"に割り当てられません は、Oracle の共有プール(Shared Pool)から必要なサイズの連続したメモリ領域を確保できないときに発生するエラーです。「共有プールが小さすぎる」だけが原因と思われがちですが、実際にはメモリの断片化(フラグメンテーション)ハードパースの多発が根本原因であることが多く、単純にサイズを増やしても再発するケースがあります。

この記事でわかること

  • ORA-04031 のエラーメッセージ4フィールドの読み方と原因の特定方法
  • 共有プールの構造(ライブラリキャッシュ・データディクショナリキャッシュ)
  • V$SGASTAT・V$SQL を使った原因調査
  • SHARED_POOL_SIZE の適切な設定と AMM/ASMM との関係
  • DBMS_SHARED_POOL.KEEP で重要オブジェクトをフラッシュされないようにする
  • ALTER SYSTEM FLUSH SHARED_POOL の使いどころと注意点
  • バインド変数・CURSOR_SHARING によるハードパース削減
スポンサーリンク

ORA-04031 エラーメッセージの読み方

ORA-04031 のメッセージには必ず4つのフィールドが含まれます。これを読み解くことで、どのプール・どの処理が原因か絞り込めます。

ORA-04031 エラーメッセージの例
ORA-04031: unable to allocate 4096 bytes of shared memory
("shared pool","SELECT emp_id, emp_name...","SQLA","sql area")

-- フィールドの構成:
-- ① 要求バイト数   : 4096 bytes
--    確保しようとした連続メモリサイズ。大きいほど深刻
-- ② プール名       : "shared pool"
--    shared pool / large pool / java pool / streams pool のどれかを確認
-- ③ ヒープ名       : "SELECT emp_id, emp_name..."
--    確保しようとした対象。SQLテキストが入ることが多い
-- ④ タイプ/サブタイプ: "SQLA" / "sql area"
--    sql area = SQL解析結果
--    PLSQL MCODE = コンパイル済みPL/SQLコード
--    KGLOB     = Library Cache Object(カーソルや定義オブジェクト)
プール名 格納内容 関連パラメータ
shared pool SQL解析結果・PL/SQLコード・データディクショナリキャッシュ SHARED_POOL_SIZE
large pool RMAN・並列クエリ・共有サーバー(Shared Server)のUGA LARGE_POOL_SIZE
java pool Oracle JVMが使用するJavaオブジェクト JAVA_POOL_SIZE
streams pool Oracle Streams・GoldenGate・LogMiner STREAMS_POOL_SIZE

発生メカニズム:「空きはあるのに割り当てられない」断片化

ORA-04031 でよく誤解されるのが、「空き容量の合計は十分でも発生する」点です。共有プールに 1MB の空きがあっても、それが 100 個の 10KB の断片に分かれていれば、100KB の連続した割り当ては失敗します。

共有プールは起動後に SQL の解析・完了を繰り返す中で、小さなフラグメントが溜まっていきます(メモリ断片化)。特に以下の状況で悪化します。

  • リテラル SQL の多用WHERE id = 1WHERE id = 2・…と異なるSQLが大量に解析・破棄されると断片化が加速する
  • 大型 PL/SQL パッケージの繰り返しロード:サイズの大きいパッケージのコンパイル済みコードがロード・アンロードされるたびに断片が生じる
  • 長時間稼働:稼働時間が長くなるほど断片化は蓄積する。週次再起動がある環境では出にくく、無停止環境で頻発しやすい

発生原因の調査方法

V$SGASTAT で共有プールの空き・使用状況を確認する
-- 共有プール全体のサイズと空き容量を確認
SELECT
    name,
    ROUND(bytes / 1024 / 1024, 2) AS mb
FROM V$SGASTAT
WHERE pool = 'shared pool'
  AND name IN ('free memory', 'library cache', 'dictionary cache',
               'sql area', 'PLSQL DIANA', 'PLSQL MCODE')
ORDER BY mb DESC;

-- 空き容量が極端に少ない(全体の5%未満)場合はサイズ不足
-- 空きが十分あっても発生する場合は断片化が原因

-- SGA全体のコンポーネント別サイズ
SELECT
    name,
    ROUND(bytes / 1024 / 1024, 2) AS mb
FROM V$SGA
ORDER BY bytes DESC;

-- 現在の SHARED_POOL_SIZE パラメータ
SELECT NAME, VALUE, DESCRIPTION
FROM V$PARAMETER
WHERE NAME IN (
    'shared_pool_size',
    'sga_target',       -- AMM/ASMM 使用時のSGA合計
    'memory_target',    -- AMM 使用時のメモリ合計
    'cursor_sharing'
);
V$SQL でハードパース多発の SQL を特定する
-- ハードパース(VERSION_COUNT が多い or PARSE_CALLS が多い)SQL を特定
-- VERSION_COUNT: 同じSQLの異なる実行計画(カーソル)の数が多い = 問題の兆候
SELECT
    sql_id,
    version_count,
    parse_calls,
    executions,
    ROUND(cpu_time / 1000000, 2) AS cpu_sec,
    ROUND(elapsed_time / 1000000, 2) AS elapsed_sec,
    SUBSTR(sql_text, 1, 80)    AS sql_preview
FROM V$SQL
WHERE version_count > 10   -- バージョン数が多いSQL
   OR (parse_calls > 100 AND executions < parse_calls * 0.5)  -- 実行より解析が多い
ORDER BY version_count DESC
FETCH FIRST 20 ROWS ONLY;

-- リテラル値の違いだけで大量の SQL が生成されていないか確認
-- sql_text の末尾の定数部分だけが違うSQLが多ければ、バインド変数未使用が原因
SELECT
    SUBSTR(sql_text, 1, 60) AS sql_prefix,
    COUNT(*) AS cursor_count
FROM V$SQL
GROUP BY SUBSTR(sql_text, 1, 60)
HAVING COUNT(*) > 50
ORDER BY cursor_count DESC;
共有プールの断片化を確認する(空きブロックのサイズ分布)
-- X$KSMSP: 共有プールの最小割り当て単位(チャンク)の状態
-- DBA権限が必要
SELECT
    ksmchcls  AS status,    -- free=空き / recr=再利用可 / R-free=解放済み
    ksmchsiz  AS chunk_size,
    COUNT(*)  AS chunks,
    ROUND(SUM(ksmchsiz) / 1024 / 1024, 2) AS total_mb
FROM X$KSMSP
GROUP BY ksmchcls, ksmchsiz
HAVING ksmchcls = 'free'
ORDER BY ksmchsiz DESC
FETCH FIRST 20 ROWS ONLY;

-- 最大の空きチャンクサイズ(これが ORA-04031 の要求サイズより小さいと失敗)
SELECT MAX(ksmchsiz) AS max_free_chunk_bytes
FROM X$KSMSP
WHERE ksmchcls = 'free';

対処法

① SHARED_POOL_SIZE を増やす

SHARED_POOL_SIZE の拡張
-- 動的パラメータのため、サービスを止めずに変更できる
ALTER SYSTEM SET SHARED_POOL_SIZE = 512M SCOPE = BOTH;
-- SCOPE = BOTH: 即時反映 + spfile に保存(推奨)

-- AMM (Automatic Memory Management) 使用中の場合
-- sga_target や memory_target を増やす
ALTER SYSTEM SET SGA_TARGET = 2G SCOPE = BOTH;

-- ASMM (Automatic Shared Memory Management) 使用中の場合:
-- sga_target > 0 かつ shared_pool_size に下限を設定する
ALTER SYSTEM SET SHARED_POOL_SIZE = 256M SCOPE = BOTH;
-- → Oracle が SGA_TARGET の範囲内で自動調整しつつ、最低 256MB は共有プールに確保する

-- 現在の実際の共有プールサイズ(V$SGA_DYNAMIC_COMPONENTS で確認)
SELECT component, ROUND(current_size / 1024 / 1024, 2) AS current_mb,
       ROUND(min_size / 1024 / 1024, 2) AS min_mb,
       ROUND(max_size / 1024 / 1024, 2) AS max_mb
FROM V$SGA_DYNAMIC_COMPONENTS
WHERE component LIKE '%shared pool%';

② DBMS_SHARED_POOL.KEEP で重要オブジェクトを固定する

頻繁に使用する大型 PL/SQL パッケージは、共有プールに常駐させることでロード・アンロードによる断片化を防げます。DBMS_SHARED_POOL.KEEP で指定したオブジェクトはフラッシュされても共有プールに固定され続けます。

DBMS_SHARED_POOL.KEEP でオブジェクトを固定する
-- 大型パッケージを共有プールに固定する
-- 引数: (名前, フラグ)
-- フラグ 'P': パッケージ・パッケージ本体(デフォルト・最もよく使う)
-- フラグ 'C': SQL カーソル('address,hash_value' 形式、V$SQL から取得)
BEGIN
    DBMS_SHARED_POOL.KEEP('DBMS_STANDARD', 'P');    -- 標準パッケージを固定
    DBMS_SHARED_POOL.KEEP('MY_BIG_PACKAGE', 'P');   -- 自作の大型パッケージ
END;
/

-- 固定されているオブジェクトを確認
SELECT name, type, kept
FROM V$DB_OBJECT_CACHE
WHERE kept = 'YES'
ORDER BY sharable_mem DESC;

-- KEEP の対象として優先度が高いオブジェクト(サイズの大きいもの)
SELECT name, type,
       ROUND(sharable_mem / 1024, 2) AS sharable_kb,
       kept
FROM V$DB_OBJECT_CACHE
WHERE type IN ('PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'FUNCTION')
  AND kept = 'NO'
ORDER BY sharable_mem DESC
FETCH FIRST 20 ROWS ONLY;
-- → サイズが大きく頻繁に使われるオブジェクトを KEEP する候補として使う

-- 固定を解除する
BEGIN
    DBMS_SHARED_POOL.UNKEEP('MY_BIG_PACKAGE', 'P');
END;
/

③ ALTER SYSTEM FLUSH SHARED_POOL(応急措置)

FLUSH SHARED_POOL は本番環境では最終手段
ALTER SYSTEM FLUSH SHARED_POOL は共有プールの内容を全クリアします。実行直後はすべての SQL・PL/SQL が再解析(ハードパース)されるため、一時的に CPU 使用率とレスポンスタイムが急激に悪化します。週次メンテナンスの直前や深夜帯の緊急対応にとどめ、根本原因を解消することが重要です。
FLUSH SHARED_POOL の実行(緊急時のみ)
-- 緊急時: 共有プールを全クリアして断片化を解消する
-- → 直後は全SQL再解析のためパフォーマンスが一時低下する
ALTER SYSTEM FLUSH SHARED_POOL;

-- 実行後は負荷が落ち着くまで V$SYSSTAT でハードパース数を監視する
SELECT name, value
FROM V$SYSSTAT
WHERE name IN ('hard parses', 'soft parses', 'parse count (total)');

④ バインド変数の使用(根本的な予防策)

ORA-04031 の最も根本的な原因の一つがリテラル SQL によるハードパースの多発です。WHERE id = 1WHERE id = 2 のように定数が違うだけの SQL はそれぞれ別の SQL として解析・保存されるため、共有プールを急速に消費します。バインド変数を使うことで SQL の再利用率を大幅に向上させられます。

リテラルSQL vs バインド変数 — 共有プールへの影響比較
-- NG: リテラルSQL(1万種類のIDに対して1万通りのSQLが共有プールに保存される)
SELECT * FROM employees WHERE employee_id = 1;
SELECT * FROM employees WHERE employee_id = 2;
SELECT * FROM employees WHERE employee_id = 3;
-- V$SQL に 3 行(= 3つの異なるSQL)が登録される

-- OK: バインド変数を使う(同じSQLが共有プールの1エントリを使いまわす)
SELECT * FROM employees WHERE employee_id = :emp_id;
-- :emp_id に 1, 2, 3 … を渡しても V$SQL の 1 エントリのみ使う

-- 現在リテラルSQLが何件あるか(同一パターンで何件の SQL が登録されているか)
SELECT
    SUBSTR(sql_text, 1, 60) AS sql_prefix,
    COUNT(*)                AS literal_count
FROM V$SQL
GROUP BY SUBSTR(sql_text, 1, 60)
HAVING COUNT(*) > 100    -- 100件以上の類似SQLがある = バインド変数未使用の疑い
ORDER BY literal_count DESC
FETCH FIRST 10 ROWS ONLY;

⑤ CURSOR_SHARING = FORCE(暫定措置)

アプリケーション改修でバインド変数に切り替えるまでの暫定措置として、CURSOR_SHARING = FORCE を設定すると、Oracle がリテラル値を自動的にバインド変数に置き換えてカーソルを共有します。

CURSOR_SHARING の設定
-- 現在の設定確認(デフォルト: EXACT)
SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME = 'cursor_sharing';

-- FORCE: Oracle がリテラルをバインド変数に自動置換してカーソルを共有する
ALTER SYSTEM SET CURSOR_SHARING = FORCE SCOPE = BOTH;

-- 注意事項:
-- ・ヒストグラムやデータ分布によっては最適でない実行計画が選ばれる場合がある
-- ・統計情報のバインド変数ピーキングの影響を受けやすくなる
-- ・根本解決にはならないため、アプリケーション改修後は EXACT に戻すことを推奨
ALTER SYSTEM SET CURSOR_SHARING = EXACT SCOPE = BOTH;   -- 正常な設定に戻す

AMM・ASMM と ORA-04031 の関係

Oracle 11g 以降では AMM(Automatic Memory Management) または ASMM(Automatic Shared Memory Management) を使うことで、共有プールを含む SGA コンポーネントのサイズを Oracle が自動調整します。ただし、AMM・ASMM は ORA-04031 を完全には防げません。

  • AMM(MEMORY_TARGET 指定):SGA + PGA 合計を Oracle が自動割り当て。共有プールが自動拡張されるが、メモリ全体の上限を超えることはできない
  • ASMM(SGA_TARGET 指定):SGA 内のコンポーネント割り当てを自動調整。共有プールが不足すると他コンポーネント(バッファキャッシュなど)から移譲する
  • 断片化はAMMでも発生する:自動管理はサイズの調整を行うが、断片化そのものを解消するわけではない。ハードパース多発が根本原因の場合は AMM でも ORA-04031 が再発する

まとめ:対処の優先順位

優先度 対処 効果と注意点
1(根本) バインド変数の使用 ハードパースを削減して共有プールの消費を根本的に抑える
2(根本) SHARED_POOL_SIZE の適正化 V$SGASTAT で実際の使用量を確認して十分なサイズに設定する
3(補助) DBMS_SHARED_POOL.KEEP 大型パッケージを固定して断片化を抑制する
4(暫定) CURSOR_SHARING = FORCE バインド変数改修までの暫定措置。本番運用には注意が必要
5(緊急) ALTER SYSTEM FLUSH SHARED_POOL 一時的な断片化解消。直後にパフォーマンス低下が発生する

SQL のパフォーマンスや V$SQL を使ったハードパース分析についてはV$SQLビューの使い方遅いSQLを特定する方法も参照してください。バインド変数の詳細と注意点はバインド変数完全ガイドで解説しています。