001package com.streamconverter.benchmark; 002 003import java.lang.management.ManagementFactory; 004import java.lang.management.MemoryMXBean; 005import java.lang.management.MemoryUsage; 006import java.time.Instant; 007import java.util.concurrent.Executors; 008import java.util.concurrent.ScheduledExecutorService; 009import java.util.concurrent.TimeUnit; 010import java.util.concurrent.atomic.AtomicLong; 011 012/** 013 * リソース使用量を監視するユーティリティクラス 014 * 015 * <p>メモリ使用量とパフォーマンス指標を測定し、大容量データ処理の効率性を検証します。 5GBデータ/50MBメモリ目標の達成を評価するために使用されます。 016 */ 017public class ResourceMonitor { 018 private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); 019 private final AtomicLong peakMemory = new AtomicLong(0); 020 private final AtomicLong currentMemory = new AtomicLong(0); 021 022 private long startTime; 023 private long startMemory; 024 private long dataSize; 025 private ScheduledExecutorService executor; 026 private volatile boolean monitoring = false; 027 028 /** Creates a new {@code ResourceMonitor}. */ 029 public ResourceMonitor() {} 030 031 /** 032 * モニタリングを開始します 033 * 034 * @param expectedDataSize 処理予定のデータサイズ(バイト) 035 */ 036 public void start(long expectedDataSize) { 037 this.dataSize = expectedDataSize; 038 this.startTime = System.currentTimeMillis(); 039 040 // 初期メモリ状態を記録 041 forceGC(); 042 this.startMemory = getCurrentMemoryUsage(); 043 this.peakMemory.set(startMemory); 044 this.currentMemory.set(startMemory); 045 046 // メモリ監視スレッドを開始 047 this.executor = 048 Executors.newSingleThreadScheduledExecutor( 049 r -> { 050 Thread t = new Thread(r, "ResourceMonitor-Thread"); 051 t.setDaemon(true); 052 return t; 053 }); 054 055 this.monitoring = true; 056 this.executor.scheduleAtFixedRate(this::updateMemoryUsage, 50, 50, TimeUnit.MILLISECONDS); 057 } 058 059 /** 060 * モニタリングを停止し、結果を返します 061 * 062 * @return リソース使用量の測定結果 063 */ 064 public ResourceUsage stop() { 065 if (!monitoring) { 066 throw new IllegalStateException("Monitor not started"); 067 } 068 069 monitoring = false; 070 if (executor != null) { 071 executor.shutdown(); 072 try { 073 executor.awaitTermination(1, TimeUnit.SECONDS); 074 } catch (InterruptedException e) { 075 Thread.currentThread().interrupt(); 076 } 077 } 078 079 // 最終測定 080 forceGC(); 081 long endTime = System.currentTimeMillis(); 082 long endMemory = getCurrentMemoryUsage(); 083 084 long duration = endTime - startTime; 085 long memoryUsed = peakMemory.get() - startMemory; 086 double throughputMBps = 087 dataSize > 0 && duration > 0 ? (dataSize / 1024.0 / 1024.0) / (duration / 1000.0) : 0.0; 088 089 return new ResourceUsage( 090 duration, 091 memoryUsed, 092 peakMemory.get(), 093 startMemory, 094 endMemory, 095 dataSize, 096 throughputMBps, 097 Instant.now()); 098 } 099 100 /** 現在のメモリ使用量を更新 */ 101 private void updateMemoryUsage() { 102 if (!monitoring) return; 103 104 long current = getCurrentMemoryUsage(); 105 currentMemory.set(current); 106 107 // ピーク値を更新 108 long peak = peakMemory.get(); 109 while (current > peak && !peakMemory.compareAndSet(peak, current)) { 110 peak = peakMemory.get(); 111 } 112 } 113 114 /** 115 * 現在のヒープメモリ使用量を取得 116 * 117 * @return ヒープメモリ使用量(バイト) 118 */ 119 private long getCurrentMemoryUsage() { 120 MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); 121 return heapUsage.getUsed(); 122 } 123 124 /** ガベージコレクションを強制実行 */ 125 private void forceGC() { 126 System.gc(); 127 System.gc(); // 2回実行してより確実にクリーンアップ 128 try { 129 Thread.sleep(100); // GC完了待機 130 } catch (InterruptedException e) { 131 Thread.currentThread().interrupt(); 132 } 133 } 134 135 /** 136 * 現在のメモリ使用量をMB単位で取得(デバッグ用) 137 * 138 * @return メモリ使用量(MB) 139 */ 140 public double getCurrentMemoryMB() { 141 return currentMemory.get() / 1024.0 / 1024.0; 142 } 143 144 /** 145 * ピークメモリ使用量をMB単位で取得(デバッグ用) 146 * 147 * @return ピークメモリ使用量(MB) 148 */ 149 public double getPeakMemoryMB() { 150 return peakMemory.get() / 1024.0 / 1024.0; 151 } 152}