Javaの実行モデルと前提
Javaの実行モデルと前提
Section titled “Javaの実行モデルと前提”Javaの実行モデルと、実務で事故を防ぐための前提条件を詳しく解説します。
実行モデルとリソースの物理的制約
Section titled “実行モデルとリソースの物理的制約”コンピュータ資源は有限であり、性能ではなく制約を前提に設計することが基本です。
主な物理的制約
Section titled “主な物理的制約”CPU・メモリよりも先に枯渇するリソース:
-
DB・外部APIのコネクション数
- 接続プールの上限(例: HikariCPの
maximumPoolSize) - 接続リークは数時間後にシステム全体を停止させる
- 接続プールの上限(例: HikariCPの
-
スレッドプール
ExecutorServiceのスレッド数制限- スレッド枯渇により、すべてのリクエストが処理できなくなる
-
ファイル記述子
- OSレベルの制限(通常1024〜65536)
- ファイルやソケットを適切にクローズしないと枯渇
実際の事故例:
10:00:00 - アプリケーション起動(接続プール: 10/10)10:00:01 - リクエスト1受信(接続取得: 11/10 → 待機)10:00:02 - リクエスト2受信(接続取得: 12/10 → 待機)...10:30:00 - 接続が解放されず、すべてのリクエストが待機状態10:30:01 - タイムアウトエラーが大量発生10:30:02 - システム全体が応答不能Javaの実行モデル
Section titled “Javaの実行モデル”JVM(Java Virtual Machine)の特徴
Section titled “JVM(Java Virtual Machine)の特徴”実行モデル:
Javaソースコード (.java) ↓ コンパイルJavaバイトコード (.class) ↓ JVMで実行ネイティブコード(実行時)重要な特徴:
- コンパイル時型チェック: コンパイル時に型エラーを検出
- ガベージコレクション: 自動メモリ管理(ただし、参照が保持されている場合は動作しない)
- マルチスレッド: ネイティブスレッドサポート(スレッドプールの管理が必要)
- 例外処理: チェック例外と非チェック例外(適切なエラーハンドリングが必要)
トランザクション境界
Section titled “トランザクション境界”Javaのトランザクション管理:
// Spring Frameworkでのトランザクション管理@Transactionalpublic void createOrder(OrderData orderData) { // トランザクション内の処理 Order order = orderRepository.save(new Order(orderData)); inventoryService.reduceStock(order.getItems()); paymentService.chargePayment(order.getId(), orderData.getAmount()); // すべて成功するか、すべてロールバック}特徴:
- 明示的なトランザクション境界:
@Transactionalアノテーションで明示 - 宣言的トランザクション管理: AOPによる自動管理
- トランザクション伝播:
REQUIRED,REQUIRES_NEW,NESTEDなど
Javaの非同期処理:
// CompletableFutureを使用CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { return externalApi.call();});
// 非同期処理は信頼できるが、エラーハンドリングが必要future.thenAccept(result -> { // 成功時の処理}).exceptionally(error -> { // エラー時の処理 return null;});特徴:
- 信頼できる非同期:
CompletableFuture、ExecutorServiceによる制御 - エラーハンドリング: 明示的なエラー処理が必要
- 再実行: 手動で実装する必要がある
実行環境による特性
Section titled “実行環境による特性”| 環境 | 特徴 | 主なリスク |
|---|---|---|
| Serverless (Lambda/Vercel) | 短寿命・自動スケール | コールドスタート、接続バースト、DBパンク、実行時間制限(Lambda: 15分、Vercel: 300秒) |
| 常駐プロセス (Spring Boot) | 長寿命・安定動作 | メモリリーク、プール断片化、デッドロック、接続リーク |
Serverless環境での実行
Section titled “Serverless環境での実行”制約:
// ❌ 悪い例: Serverless環境で問題のあるコード@RestControllerpublic class OrderController { @PostMapping("/orders") public Order createOrder(@RequestBody OrderData orderData) { // 問題: 長時間実行される可能性がある // 問題: トランザクションが長時間保持される // 問題: 接続プールが適切に管理されない return orderService.createOrder(orderData); }}問題点:
- 実行時間の制限: Lambdaは最大15分、Vercelは最大300秒
- コールドスタート: JVMの起動に時間がかかる(1-3秒)
- メモリ制限: メモリ使用量に制限がある(Lambda: 128MB〜10GB)
- 接続バースト: スケールアウト時に接続プールが急増し、DBがパンクする可能性
解決策:
// ✅ 良い例: Serverless環境に適したコード@RestControllerpublic class OrderController { @PostMapping("/orders") public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderData orderData) { // 1. バリデーション(短時間) validateOrderData(orderData);
// 2. 注文を作成(短時間) Order order = orderService.createOrder(orderData);
// 3. 非同期処理をキューに投入 messageQueue.send("order.created", order.getId());
// 4. 即座にレスポンスを返す return ResponseEntity.accepted() .body(new OrderResponse(order.getId(), "PROCESSING")); }}常駐プロセス環境での実行
Section titled “常駐プロセス環境での実行”Spring Bootアプリケーション
Section titled “Spring Bootアプリケーション”特徴:
// 常駐プロセス環境での実行@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}メリット:
- 長時間実行可能: 実行時間の制限がない
- 接続プール: データベース接続プールを保持
- キャッシュ: メモリキャッシュを保持
- バックグラウンド処理:
@Scheduled、@Asyncによる処理
実装例:
// ✅ 良い例: 常駐プロセス環境に適したコード@Servicepublic class OrderService { @Autowired private OrderRepository orderRepository;
@Autowired private PaymentService paymentService;
@Transactional public Order createOrder(OrderData orderData) { // トランザクション内で処理 Order order = orderRepository.save(new Order(orderData)); paymentService.chargePayment(order.getId(), orderData.getAmount()); return order; }
@Async public CompletableFuture<Void> processOrderAsync(Long orderId) { // 非同期処理 return CompletableFuture.runAsync(() -> { // 長時間実行される処理 processOrder(orderId); }); }}Javaの実行モデルと前提のポイント:
- リソースの物理的制約: CPU・メモリよりも先に枯渇するのは、DB接続数・スレッドプール・ファイル記述子
- JVM: コンパイル時型チェック、ガベージコレクション、マルチスレッド
- トランザクション境界:
@Transactionalで明示、宣言的管理 - 非同期処理:
CompletableFutureによる制御、エラーハンドリングが必要 - Serverless環境: 実行時間制限、コールドスタート、メモリ制限、接続バースト
- 常駐プロセス環境: 長時間実行可能、接続プール、キャッシュ、バックグラウンド処理(メモリリーク・接続リークに注意)
重要な原則: 性能ではなく制約を前提に設計する。リソースの垂れ流しは数時間後にシステム全体を停止させる。