「2つともキャッシュ設定なのに、何が違うの?」
「値を大きくすると、何が増えるの?」

implicitStatementCacheSizeは準備済みのSQL文、maxCachedBufferSizeはドライバ内部の作業用バッファを再利用するための設定です。名前は似ていますが、再利用する対象が違います。
まずは、前者がSQL文の準備作業、後者がメモリ上の作業スペースに関する設定だと理解できればOKです。
キャッシュという仕組み自体を先に整理したい場合は、キャッシュとは?も参考にしてください。この記事では、同じ検索SQLを何度も実行するWebアプリケーションを例に、2つの設定がどこで働くのかを順番に説明します。

implicitStatementCacheSizeはStatementの「個数」、maxCachedBufferSizeは内部バッファの「大きさ」の上限です。どちらも物理接続ごとに、次回使えるものを残します。最初に2つの違いを整理
| 設定 | 再利用するもの | 値の意味 | 主な狙い |
|---|---|---|---|
implicitStatementCacheSize |
PreparedStatementやCallableStatement |
1接続あたりに保持する文の最大数 | 同じSQLを再び準備する処理を減らす |
maxCachedBufferSize |
Oracle JDBCドライバ内部のbyte/charバッファ | 再利用用に保持する最大サイズ(30以下は底2の対数、30超は実サイズ) | 大きなバッファを抱え続けることによるメモリ消費を抑える |

この表だけでは、まだ2つの違いがピンと来ないかもしれません。そこで、同じ商品検索SQLを2回実行する流れを追いながら、どこで何が再利用されるのかを見てみましょう。
まずはPreparedStatementが何を準備しているか
次のSQLを、商品検索画面から何度も実行するとします。
SELECT product_id, product_name, price FROM products WHERE category_id = ?
?には検索対象のカテゴリIDが入ります。Java側では、概念的に次のような流れになります。
String sql = """
SELECT product_id, product_name, price
FROM products
WHERE category_id = ?
""";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setInt(1, 10);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
// 検索結果を1行ずつ処理する
}
}
}
処理が終わるたびに、これらをすべて捨てて次回また準備すると、同じSQLを繰り返すアプリケーションでは無駄が増えます。そこで使われるのが、Statementキャッシュです。
implicitStatementCacheSizeは「SQL文の準備済みセット」の上限
implicitStatementCacheSizeは、Oracle JDBCドライバの暗黙的Statementキャッシュへ、準備済みの文を1接続あたり何個まで保持するかを指定します。
暗黙的キャッシュを有効にすると、アプリケーションがPreparedStatement.close()を呼んだとき、対象の文は直ちに物理的に破棄されず、再利用できる状態でキャッシュへ戻ります。
- 最初の
prepareStatement(sql)で文を準備する - SQLを実行する
close()でStatementキャッシュへ戻す- 同じ接続で同じSQLを再び準備すると、キャッシュ済みの文を再利用する
たとえば値を50にすると、物理接続ごとに最大50個の文を保持できます。コネクションプールが20接続なら、単純な全体上限のイメージは「最大50文 × 20接続」です。キャッシュはアプリケーション全体で1個ではなく、接続ごとに持つ点が重要です。
キャッシュが満杯になると、Oracle JDBCでは最近使われていない文が追い出されます。また、SQL文字列が少しでも異なれば、別の文として扱われる場合があります。
大きくすればするほど速くなるわけではない
Statementキャッシュには、データベース側のカーソルなどの資源も関係します。値を大きくしすぎると、接続数との掛け算で保持数が膨らみ、OPEN_CURSORSの上限やメモリ消費へ影響します。
Oracleの現行ドキュメントでは、接続プロパティの既定値は0です。つまり、設定しなければこの接続プロパティによる暗黙的Statementキャッシュは無効です。値は「よく使うSQLの種類数」と「物理接続数」を見ながら決めます。
maxCachedBufferSizeは「SQL結果そのもの」のキャッシュではない
同じ検索SQLでも、結果が10行のときと10万行のときでは、JDBCドライバがデータを受け取るために必要な作業用メモリが変わります。
Oracle JDBCドライバは、内部でbyte配列やchar配列などのデータバッファを使います。一度確保したバッファを再利用できれば、次の実行で毎回新しく確保する回数を減らせます。
しかし、ある1回だけ巨大な検索結果を扱ったために大きなバッファを確保し、そのバッファを接続が生きている間ずっと保持すると、ヒープを圧迫する可能性があります。
上限より大きなバッファが必要なSQLも実行できます。ただし、その大きなバッファを再利用用として残さないため、次回また必要になれば再確保が発生し、性能とメモリ使用量のトレードオフが生まれます。
maxCachedBufferSizeの値はどう読む?
Oracle JDBC 21cのAPIリファレンスでは、maxCachedBufferSizeを「ドライバがキャッシュする最大の内部char/byteバッファサイズについて、底を2とする対数」と定義しています。たとえば20なら、220が上限の目安です。
| 設定値 | 2の累乗 | byteバッファで考えた目安 |
|---|---|---|
18 |
218 | 256KiB |
19 |
219 | 512KiB |
20 |
220 | 1MiB |
30 |
230 | 1GiB |
1048576 |
30を超えるため実サイズ | 1MiB |
ただし、公式APIでは30を超える値は、底を2とする対数ではなく実際のバッファサイズとして扱うとされています。byteバッファではbyte数、charバッファではchar数です。char配列が占めるヒープ量はbyte配列と同じとは限らないため、表はヒープ消費量を厳密に示すものではありません。
Oracle JDBC 21cおよび26aiのAPIリファレンスでは、既定値は30です。なお、12未満はバッファキャッシュを事実上無効化します。製品固有のチューニングガイドで別の推奨値が示される場合もあるため、利用中のJDBCドライバの版と対象製品の公式手順を優先してください。
同じSQLを2回実行すると、2つの設定はどこで働く?
先ほどの商品検索を、同じ物理接続で2回実行する場面へ戻ります。
1回目
- SQL文を準備し、Statementを作る
- 検索結果を受け取るため、内部バッファを確保する
- 結果をJavaへ返す
- Statementと再利用可能なバッファをキャッシュへ戻す
2回目
implicitStatementCacheSizeの範囲内なら、準備済みStatementを再利用するmaxCachedBufferSizeの範囲内なら、内部バッファも再利用できる- 上限を超えて保持されなかったバッファは、必要に応じて再確保する
つまり、2つは同じSQL実行の中で働きますが、再利用する階層が違います。Statementを再利用できても、巨大なバッファまで保持する必要はありません。反対に、バッファを再利用できても、別のSQL文ならStatementの再利用とは別問題です。
設定例
JDBC接続プロパティとして指定する場合の概念例です。
Properties props = new Properties();
props.setProperty("user", "app_user");
props.setProperty("password", "********");
// 1接続あたり50文まで、暗黙的Statementキャッシュへ保持
props.setProperty("oracle.jdbc.implicitStatementCacheSize", "50");
// 2^20を超える内部バッファは、再利用用として保持しない
props.setProperty("oracle.jdbc.maxCachedBufferSize", "20");
Connection connection =
DriverManager.getConnection(jdbcUrl, props);
実際の指定場所は、アプリケーションサーバー、コネクションプール、フレームワークによって異なります。
現場では何を見て調整するか
値だけを見て「大きいから良い」「小さいから安全」とは判断できません。それぞれ、症状と観測対象を分けます。
implicitStatementCacheSizeを確認する場面
- 同じSQLを何度も実行しているのに、SQL準備のコストが目立つ
- Statementキャッシュのヒット率を確認したい
ORA-01000: maximum open cursors exceededとの関係を切り分けたい- 物理接続数を増やした後に、カーソル数やメモリが増えた
キャッシュ数だけでなく、コネクションプールの物理接続数、SQLの種類数、データベースのOPEN_CURSORSをセットで確認します。
maxCachedBufferSizeを確認する場面
- 巨大な検索やLOB処理の後、Javaヒープ使用量が戻りにくい
- JFRやヒープダンプで、Oracle JDBC内部の大きなbyte/char配列が目立つ
- GC(ガベージコレクション)の回数や停止時間が増えている
- 値を下げた後に、同じ大規模SQLの処理時間が悪化した
Oracleの古いJDBC READMEでも、まず各Statementのフェッチサイズを適切に設定し、それが難しい場合にmaxCachedBufferSizeでメモリフットプリントを抑える考え方が示されています。したがって、巨大な取得結果が原因なら、設定値だけでなくfetchSizeやSQL自体も調査対象です。
よくある誤解
「2つともSQL結果を速くするキャッシュ」ではない
implicitStatementCacheSizeは、準備済みのSQL文を再利用します。maxCachedBufferSizeは、ドライバ内部の作業用メモリをどこまで再利用用に残すかを決めます。検索結果の内容そのものを保存するResult Cacheとは別物です。
maxCachedBufferSizeを小さくしても、SQLの取得量は減らない
この設定は「大きな結果を取得禁止にする上限」ではありません。大きなバッファを使った後、それをキャッシュへ残すかどうかに関係します。転送量を減らしたいなら、SQLの抽出条件、取得列、ページング、フェッチサイズなどを見直します。
implicitStatementCacheSizeだけでデータベースの解析がゼロになるとは限らない
JDBCドライバ側のStatement再利用と、Oracle Database側の共有SQLやカーソル管理は関連しますが同一ではありません。性能問題を調べるときは、Java側の設定だけでなく、AWRやSQL統計でparse回数、実行回数、待機時間も確認します。
まとめ
JavaやJDBCの前提から確認したい場合は、Java学習ロードマップもあわせてご覧ください。
