mcpField
J
java-profiling

java-profiling

github.com/claude-dev-suite開発4644 回インストール2 日前に更新
リポジトリ

java-profiling について

java-profilingは、JVMの性能を診断するエンジニア向けのリファレンススキルです。JFR、jcmd、ガベージコレクション、メモリ、CPU、JITの分析方法とトラブルシューティングを扱います。

主な機能

  1. 01JFRの記録と分析
  2. 02jcmdによるプロセス・メモリ診断
  3. 03GCの選択、ログ、チューニング
  4. 04CPU・割り当て・ロックの分析
  5. 05JITとメモリのトラブルシューティング

活用シーン

  1. 01高CPU使用率やスレッド競合の調査
  2. 02ヒープやメタスペース枯渇の診断
  3. 03ガベージコレクション停止時間の確認
  4. 04メモリ割り当てやロック動作の分析

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill java-profiling

インストール後、Claude Code で/java-profilingを入力して読み込まれたか確認します。

詳細

Java/JVM パフォーマンスプロファイリング

このスキルを使用しない場合

  • Node.js/JavaScript のプロファイリング - V8 プロファイラーとヒープ分析には nodejs-profiling スキルを使用してください
  • Python のプロファイリング - cProfile と tracemalloc には python-profiling スキルを使用してください
  • アプリケーションレベルの最適化 - これは JVM レベルのプロファイリング用であり、アルゴリズムの最適化用ではありません
  • データベースクエリのチューニング - データベース固有のプロファイリングツールを使用してください
  • フロントエンドのパフォーマンス - クライアントサイドのプロファイリングにはブラウザーの DevTools を使用してください

詳細な知識:包括的な JFR 設定、GC チューニング、JVM 診断については、テクノロジーに java を指定して mcp__documentation__fetch_docs を使用してください。

Java Flight Recorder(JFR)

JFR の開始
# Start recording with application
java -XX:+FlightRecorder \
     -XX:StartFlightRecording=duration=60s,filename=recording.jfr \
     -jar app.jar

# Start recording on running JVM
jcmd <pid> JFR.start duration=60s filename=recording.jfr

# Continuous recording (always-on)
java -XX:+FlightRecorder \
     -XX:FlightRecorderOptions=stackdepth=256 \
     -XX:StartFlightRecording=disk=true,maxsize=500m,maxage=1d \
     -jar app.jar

# Dump current recording
jcmd <pid> JFR.dump filename=dump.jfr
JFR の設定
# custom.jfc
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0">
  <event name="jdk.CPULoad">
    <setting name="enabled">true</setting>
    <setting name="period">1 s</setting>
  </event>
  <event name="jdk.GCHeapSummary">
    <setting name="enabled">true</setting>
  </event>
  <event name="jdk.ObjectAllocationInNewTLAB">
    <setting name="enabled">true</setting>
    <setting name="stackTrace">true</setting>
  </event>
</configuration>
JFR ファイルの分析
# Print JFR summary
jfr summary recording.jfr

# Print specific events
jfr print --events jdk.CPULoad recording.jfr
jfr print --events jdk.ExecutionSample --json recording.jfr

# Export to JSON
jfr print --json recording.jfr > recording.json

jcmd 診断

プロセス情報
# List all Java processes
jcmd

# VM info
jcmd <pid> VM.version
jcmd <pid> VM.flags
jcmd <pid> VM.system_properties
jcmd <pid> VM.command_line

# Thread dump
jcmd <pid> Thread.print

# Heap info
jcmd <pid> GC.heap_info

# Class histogram
jcmd <pid> GC.class_histogram
メモリ分析
# Native memory tracking (requires -XX:NativeMemoryTracking=summary)
jcmd <pid> VM.native_memory summary

# Heap dump
jcmd <pid> GC.heap_dump /path/to/dump.hprof

# Force GC
jcmd <pid> GC.run

GC チューニング

GC の選択
# G1GC (default in JDK 9+, recommended for heap > 4GB)
java -XX:+UseG1GC -jar app.jar

# ZGC (low latency, JDK 15+)
java -XX:+UseZGC -jar app.jar

# Shenandoah (low latency, OpenJDK)
java -XX:+UseShenandoahGC -jar app.jar

# Parallel GC (throughput)
java -XX:+UseParallelGC -jar app.jar
GC ログ
# JDK 9+ unified logging
java -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=5,filesize=10m \
     -jar app.jar

# Common GC flags
java -XX:+PrintGCDetails \
     -XX:+PrintGCDateStamps \
     -XX:+PrintTenuringDistribution \
     -Xloggc:gc.log \
     -jar app.jar
G1GC のチューニング
java -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \        # Target pause time
     -XX:G1HeapRegionSize=16m \        # Region size
     -XX:InitiatingHeapOccupancyPercent=45 \  # Start marking at 45%
     -XX:G1ReservePercent=10 \         # Reserve for promotions
     -XX:ConcGCThreads=4 \             # Concurrent GC threads
     -XX:ParallelGCThreads=8 \         # Parallel GC threads
     -jar app.jar

メモリ最適化

ヒープサイズの設定
# Set heap size
java -Xms4g -Xmx4g -jar app.jar  # Fixed heap (recommended for production)

# Metaspace sizing
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar app.jar

# Direct memory
java -XX:MaxDirectMemorySize=256m -jar app.jar
メモリリークの検出
// Common leak patterns

// ❌ Bad: Static collections that grow
private static List<Object> cache = new ArrayList<>();
public void process(Object obj) {
    cache.add(obj);  // Never removed
}

// ✅ Good: Bounded cache with eviction
private static final Cache<String, Object> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(Duration.ofMinutes(10))
    .build();

// ❌ Bad: Unclosed resources
public void readFile(String path) {
    InputStream is = new FileInputStream(path);
    // Missing is.close()
}

// ✅ Good: Try-with-resources
public void readFile(String path) {
    try (InputStream is = new FileInputStream(path)) {
        // Process
    }
}

// ❌ Bad: ThreadLocal not cleaned
private static ThreadLocal<Connection> connHolder = new ThreadLocal<>();
public void process() {
    connHolder.set(getConnection());
    // Missing connHolder.remove()
}

// ✅ Good: Always clean ThreadLocal
public void process() {
    try {
        connHolder.set(getConnection());
        // Process
    } finally {
        connHolder.remove();
    }
}

CPU プロファイリング

async-profiler(推奨)
# Profile CPU
./profiler.sh -d 30 -f profile.html <pid>

# Profile allocations
./profiler.sh -d 30 -e alloc -f alloc.html <pid>

# Profile locks
./profiler.sh -d 30 -e lock -f lock.html <pid>

# Flame graph output
./profiler.sh -d 30 -f flamegraph.html -o flamegraph <pid>
JMC(Java Mission Control)
# Open JFR recording in JMC
jmc recording.jfr

よくあるボトルネックのパターン

同期の問題
// ❌ Bad: Coarse-grained locking
public synchronized void process(String key, Object value) {
    cache.put(key, value);
    compute(value);
}

// ✅ Good: Fine-grained locking
private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
public void process(String key, Object value) {
    cache.put(key, value);  // Lock-free for different keys
    compute(value);
}

// ✅ Good: Read-write lock
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public Object get(String key) {
    lock.readLock().lock();
    try { return cache.get(key); }
    finally { lock.readLock().unlock(); }
}
文字列操作
// ❌ Bad: String concatenation in loop
String result = "";
for (String s : list) {
    result += s;  // Creates new String each iteration
}

// ✅ Good: StringBuilder
StringBuilder sb = new StringBuilder();
for (String s : list) {
    sb.append(s);
}
String result = sb.toString();

// ✅ Good: String.join for simple cases
String result = String.join("", list);
コレクションの最適化
// ❌ Bad: ArrayList when size known
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    list.add(getData(i));  // Multiple resizes
}

// ✅ Good: Pre-size collections
List<String> list = new ArrayList<>(10000);

// ✅ Good: Use primitive collections for performance
// Use Eclipse Collections, Trove, or fastutil
IntList list = new IntArrayList(10000);
ボクシング/アンボクシング
// ❌ Bad: Autoboxing in hot path
public long sum(List<Long> numbers) {
    long sum = 0;
    for (Long n : numbers) {
        sum += n;  // Unboxing each iteration
    }
    return sum;
}

// ✅ Good: Use primitive streams
public long sum(List<Long> numbers) {
    return numbers.stream().mapToLong(Long::longValue).sum();
}

// ✅ Good: Primitive arrays when possible
public long sum(long[] numbers) {
    return Arrays.stream(numbers).sum();
}

JIT 最適化

ウォームアップ
// Warm-up critical paths before measuring
public static void main(String[] args) {
    // Warm-up phase
    for (int i = 0; i < 10_000; i++) {
        criticalMethod(i);
    }

    // Measurement phase
    long start = System.nanoTime();
    for (int i = 0; i < 100_000; i++) {
        criticalMethod(i);
    }
    long duration = System.nanoTime() - start;
}
JIT ログ
# Print JIT compilation
java -XX:+PrintCompilation -jar app.jar

# Print inlining decisions
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -jar app.jar

# Disable specific optimizations for debugging
java -XX:+UnlockDiagnosticVMOptions -XX:DisableIntrinsic=_hashCode -jar app.jar

プロファイリングのチェックリスト

確認項目ツールコマンド
CPU ホットスポットJFRjcmd <pid> JFR.start
メモリ使用量jcmdjcmd <pid> GC.heap_info
GC の動作GC ログ-Xlog:gc*
スレッドの競合JFRjdk.JavaMonitorWait イベント
メモリリークヒープダンプjcmd <pid> GC.heap_dump
クラスの読み込みjcmdjcmd <pid> VM.classloaders

本番環境向けフラグ

java \
  -server \
  -Xms4g -Xmx4g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:+UseStringDeduplication \
  -XX:+AlwaysPreTouch \
  -XX:+DisableExplicitGC \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/var/log/heap.hprof \
  -Xlog:gc*:file=/var/log/gc.log:time,uptime:filecount=5,filesize=10m \
  -jar app.jar

アンチパターン

アンチパターン問題点適切な方法
ホットパスでの System.out.println()極端に遅く、同期 I/O が発生します非同期アペンダーを備えた SLF4J を使用してください
寿命の短いオブジェクトを大量に作成するGC の負荷と割り当てのオーバーヘッドが増加しますオブジェクトを再利用するか、オブジェクトプールを使用してください
ホットパスでの synchronizedスレッド競合が発生し、スケーラビリティが低下しますConcurrentHashMap、ReentrantLock、またはロックフリーのデータ構造を使用してください
ループ内で + による文字列連結を行う中間文字列が大量に生成されますStringBuilder を使用してください
ループ内でオートボクシングを行うラッパーオブジェクトが生成されますプリミティブ型を使用してください
コレクションのサイズを事前に設定しない頻繁なサイズ変更が発生し、メモリが無駄になりますnew ArrayList<>(expectedSize) で事前にサイズを指定してください
クリーンアップに finalize() を使用する動作が予測できず、非推奨ですtry-with-resources または明示的なクリーンアップを使用してください
GC ログを無視するパフォーマンスの低下を見逃します本番環境では常に GC ログを有効にしてください
あらゆる用途に同じヒープ設定を適用するワークロードに適さない GC 一時停止が発生しますアプリケーションの動作に応じてヒープを調整してください
コネクションプールを使用しない接続の作成にオーバーヘッドがかかりますHikariCP などを使用してください

クイックトラブルシューティング

問題診断解決策
GC の長い一時停止ヒープが大きすぎる、または GC が不適切ですZGC/Shenandoah を使用するか、G1GC の一時停止目標を調整します
OutOfMemoryError: Java heap spaceメモリリーク、またはヒープが不足していますヒープダンプを分析し、必要に応じて -Xmx を増やします
OutOfMemoryError: Metaspace読み込まれたクラスが多すぎます-XX:MaxMetaspaceSize を増やすか、クラスローダーのリークを修正します
CPU 使用率が高いホットループ、非効率なアルゴリズムJFR または async-profiler で CPU プロファイルを取得します
スレッドの競合ロックの競合スレッドダンプを分析し、ロックの範囲を縮小します
起動が遅いクラスの読み込み、初期化AppCDS、遅延初期化を使用します
メモリリークリソースの未クローズ、静的コレクションヒープダンプを比較し、増加しているオブジェクトを特定します
GC オーバーヘッド上限を超過GC が時間の 98% 超を占めていますメモリリークを修正するか、ヒープを増やします
Full GC の頻度が高すぎるOld 世代がいっぱいになっていますヒープ比率を調整し、オブジェクトの昇格に関する問題を修正します
アプリケーションが応答しないデッドロック、または長い GCスレッドダンプでデッドロックを特定し、GC ログを確認します

関連スキル

コメント