0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

jax-rs チューニング

Last updated at Posted at 2025-10-28

JAX-RS における JIT再コンパイル・GCスパイク改善日記

前提情報

JAX-RS構成、Jersey依存軽減、@ApplicationScopedリソース、JAXBContextResolverによるContext制御、MOXy→Jackson検証済)

主原因の予想

「JIT再コンパイルやGCスパイクを引き起こす要素」は主にオブジェクト生成頻度・反射呼び出し・Context構築コストにあります。

以下に JSONパターン(Jackson/MOXy)XMLパターン(JAXB/MOXy) の両方での改善ポイントと、見込み改善度を整理します。


🔷 JSONパターン(MOXy → Jackson)

現状

  • MOXy は JAXB ベースであり、JSON 変換時にも JAXBContext を利用。
  • JAXBContext の初期化・メタデータ生成が重く、反射を多用。
  • JSON変換時に XmlElement アノテーションを参照するため、ClassMetadata構築コストが高い。

改善案と効果

改善項目 内容 改善見込み 期待される改善秒数(GCスパイク抑制効果)
✅ Jackson へ完全移行 すでに試行済。MOXyより軽量でJIT安定。 ★★★★☆ 約 100〜300ms 短縮(1秒スパイクの原因を軽減)
✅ ObjectMapper を @Singleton 管理 各リクエストで再生成しない。 ★★★★★ 反射呼び出し削減、GC頻度を1/5以下に抑制可能
@JsonProperty 明示付与 フィールド名自動解析の反射コストを低減。 ★★★☆☆ 数十ms程度改善(安定化効果)
✅ ObjectWriter のキャッシュ 同一型のシリアライズ設定を再利用。 ★★★★☆ 100ms前後の短縮、JIT再コンパイルも抑制
✅ ObjectMapper.disable(FAIL_ON_UNKNOWN_PROPERTIES) エラーハンドリングでの例外発生を防止。 ★★☆☆☆ 安定化(スパイク時のGC圧縮負荷軽減)

💡 効果まとめ

Jackson化により「初回リクエストでのクラス解析」と「GC発生時のJIT再最適化(deopt)」が大幅に減少。

体感では GCスパイク 1.5〜2秒 → 0.5秒未満 程度まで改善可能。


🔶 XMLパターン(JAXB / MOXy)

現状

  • JAXBContextResolver でクラス単位のContextを生成している(これは非常に良い実装)。
  • ただし、Context内部ではリフレクションキャッシュや Unmarshaller の再生成が発生。
  • スレッドごとにUnmarshallerを都度生成している場合、GC圧迫&JIT再最適化が起きやすい。

改善案と効果

改善項目 内容 改善見込み 期待される改善秒数(GCスパイク抑制効果)
✅ JAXBContext のシングルトン化 Resolverで既に対応済み。 ★★★★★ 初期化コスト削減、リクエスト毎再構築なし
✅ Unmarshaller / Marshaller の再利用 ThreadLocalではなくPool管理(例:BlockingQueue)。 ★★★★☆ 反射負荷削減、約0.3〜0.5秒短縮
@XmlElement の明示指定 リフレクション探索コスト減少。 ★★★☆☆ 安定性向上(ClassMetadata構築削減)
✅ MOXy → 標準JAXB実装に変更 EclipseLink(MOXy)は内部キャッシュ肥大化あり。 ★★★★☆ 大量スレッド時のGC頻度低下(最大0.5秒改善)
✅ 不要なXMLAdapter削除 XmlAdapter はProxy生成を伴うため頻繁なJIT対象。 ★★☆☆☆ 数十msの安定化効果

💡 効果まとめ

XML処理はJSONより重いが、Unmarshaller/Marshallerの再利用 + MOXy脱却 で劇的改善。

GCスパイクが1秒以上出ていたケースで 0.4秒前後まで短縮 例あり。


📊 総合改善見込み一覧

項目 改善見込み 主な原因削減ポイント
Jackson化(JSON系) ★★★★★ JAXBContext生成・反射解析を削除
JAXBContext再利用(XML系) ★★★★★ 初期化反射の再実行防止
Unmarshallerプール化 ★★★★☆ JIT再最適化対象削減
Moxy削除 ★★★★☆ Proxy層削減・リフレクション軽減
アノテーション明示化 ★★★☆☆ メタデータ構築コスト削減
ロガー改善 ★★★☆☆ 非同期化によるGC安定

🎯 次のステップ提案

もし次のステップを提案するなら:

  1. JSON系では ObjectMapper を完全シングルトン化し、ObjectReader/ObjectWriter をキャッシュ化。
  2. XML系では Unmarshaller / Marshaller をスレッドセーフプールで再利用する仕組みを導入。
  3. Moxy削除(標準JAXBへ) を検証(特に weblogic.jaxb or com.sun.xml.bind )

🔧 GCチューニング設定例

# Young世代を大きくしてOld昇格を防ぐ
-XX:NewRatio=1           # Young:Old = 1:1(デフォルトは1:2)
-XX:SurvivorRatio=6      # Eden:Survivor = 6:1(デフォルトは8:1)
-XX:MaxTenuringThreshold=15  # Old昇格までのGC回数を最大に

ログから以下を確認

grep "Pause Young" gc.log | wc -l      # Young GC回数
grep "Pause Mixed" gc.log | wc -l      # Mixed GC回数(減るべき)
grep "promotion failed" gc.log         # 昇格失敗(ゼロが理想)
# Old世代の使用率推移
grep "Old:" gc.log

→ 徐々に増える場合は設定不足

🔧 Bean Validation を明示的に無効化

// Jersey設定内で
property("jersey.config.beanValidation.disable.server", true);

🔧 Validatorのプロデュース

@ApplicationScoped
public class ValidatorProducer {

    private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    private final Validator validator = factory.getValidator();

    @Produces
    @ApplicationScoped
    public Validator produceValidator() {
        return validator;
    }
}

🔍 問題の本質:何が悪いのか

結論から言うと、「1つの機能が悪い」というより、Jersey + WebLogic + JAXB/MOXy + JSON-B/Jackson + CDI/HK2 の "複合要因"で、
JDK11 の GC と JIT の挙動が噛み合って クラス解決とアンロードが暴発している状態です。

✅ 何が悪いのか(本質)

システムの問題の核はこれです:
自動ディスカバリ(オートスキャン) と マルチスタック併用で、
大量のクラス解決・リフレクションが発生し、
Mixed GC の Class Unloading タイミングで一気にパージされる
そして
スレッド毎に JSON/Validation インフラが再生成されていた

カテゴリ 問題
Jersey プロバイダ自動検出、HK2、Validation自動起動
JSON MOXy + JSON-B + Jackson 混在
JAXB 自動 Context 探索動作
CDI/HK2 混在でインジェクション経路が増殖
GC/JIT JDK11 の Class Unloading が重く動作
JNI/JNA Cモジュールもクラスローダーにぶら下がる

→ この組み合わせが、
request毎に invisible な 反射・Registry 解決 → メモリ増加 → GCで爆発
という流れを作っていました。

✅ それはなぜ JDK8 では無事だったのか?

JDK11 から GC の仕様が変わっています:

項目 JDK8 JDK11
Class Unloading 遅いけど安定 ZGC/G1で頻繁化・明示化
JIT deopt 少ない プロファイル閾値が厳密化
Reflection Cache 比較的残る GCで積極的にパージ

JDK11 = class metadata flush がガチ
→ 自動検出 & リフレクション多用コードは不利

✅ なぜ GC 時だけスパイク?

「普段は速いが mixedGC で 2秒スパイク」

理由:
1. Request中に Provider/Context が積み上がる
2. Mixed GC で "クラスメタ" が大量に破棄
3. 次の request でそれを再生成
4. → resolve flood / oop flood → pause

✅ 発見された具体的悪玉

項目 悪さの内容
JSON-B毎回create スレッド毎に大きなオブジェクト生成
MOXy context 大量の JAXB metadata
Bean Validation 気づかない自動起動で reflection
Jersey Provider AutoScan ClassLoader探索 → cache肥大
CDI + HK2 依存解決経路が増える
JNI(JNA) クラスローダツリーに乗る

どれも「単独なら問題ない」が
全部積むと JDK11 の GCと衝突する。

✅ だから改善策は何を狙っている?

改善内容 狙い
Provider手動登録 自動解決を殺して reflection止める
JSON/XML一元化 Serializer stackを固定
シングルトン化 毎回の巨大allocを排除
Validation OFF or hand-rolled 自動trigger排除
JAXB/MOXy除外 Class metadata削減
CDI or HK2どちらかに固定 Dependency graph縮小
AutoScan無効 Class scanning停止

→ GCで Class metadata を掃き出さない構成に変えている

✅ なぜまだ残る問題があるのか?

「少し改善したが決定打ではない」

→ オートディスカバリ系がまだ完全に殺せてない可能性が高い

特に、

  • ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE
  • METAINF_SERVICES_LOOKUP_DISABLE

この2つ未設定のままだと Jersey は水面下で勝手に探し続ける

✅ 最後に:一言で言うと?

負荷じゃなく、自動化された "クラス探索+リフレクション+キャッシュ破棄" が悪い

beanもpomから除外
<dependency>
  <groupId>org.glassfish.jersey.core</groupId>
  <artifactId>jersey-server</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.glassfish.jersey.media</groupId>
      <artifactId>jersey-media-moxy</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.glassfish.jersey.ext</groupId>
      <artifactId>jersey-bean-validation</artifactId>
    </exclusion>
  </exclusions>
</dependency>

✅ WebLogic利用は本来どうか?(JDK11 + javax + WebLogic)

本来避けるべき

理由 内容
レガシー化 WebLogicは Jakarta EE 以降の対応が遅い
クラウド非最適 コンテナ/K8s時代と相性が悪い
起動/デプロイ重い Microservice向きでない
JAX-RS/MOXy問題 今回苦労したような依存の泥沼

✅ "本来"どう設計すべき?

レイヤ 推奨
フロント SPA(Vue/React/Next.js)
API Helidon / Quarkus / Spring Boot
Java API jakarta.*
認証/Session Token認証 / OAuth2 / OpenID Connect
デプロイ Docker / K8s(EKS/AKS/GKE)
Messaging Kafka / MQ
APサーバ ❌ WebLogic → ✅軽量実装

最終系ではWebLogicは脱却がベスト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?