# 機能設計書 93-Unsafe Operations

## 概要

本ドキュメントは、Apache Sparkの低レベルメモリ操作機能（Unsafe Operations）の設計について記述する。sun.misc.UnsafeをラップしたPlatformクラスとメモリブロック管理を通じて、Sparkの内部データ処理に必要な高速メモリ操作を提供する。

### 本機能の処理概要

**業務上の目的・背景**：Sparkの内部処理（特にTungstenエンジン）では、JVMのヒープメモリだけでなくオフヒープメモリも含めた効率的なメモリ管理が不可欠である。Javaの標準APIでは提供されないダイレクトメモリアクセスやメモリコピー操作を、sun.misc.Unsafeを安全にラップして提供する。

**機能の利用シーン**：Tungsten実行エンジンでのソート処理、UnsafeRowによるバイナリ行表現、タスクメモリマネージャによるメモリページ管理、ダイレクトバッファ割り当てなどで利用される。

**主要な処理内容**：
1. Platformクラスによるsun.misc.Unsafeの安全なラップ
2. 各種プリミティブ型（int, long, float, double等）のダイレクトメモリ読み書き
3. メモリ確保（allocateMemory）、解放（freeMemory）、再確保（reallocateMemory）
4. メモリコピー（copyMemory）のオーバーラップ対応実装
5. DirectByteBufferのJVM MaxDirectMemorySize制限バイパス割り当て
6. MemoryBlockによるメモリブロックの論理管理（ページ番号、サイズ管理）
7. Unalignedアクセスの可否判定

**関連システム・外部連携**：Sparkの内部モジュール（SQL Tungsten, Shuffle, UnsafeRow等）から利用される基盤モジュール。JVMの内部API（sun.misc.Unsafe, jdk.internal.ref.Cleaner）に依存。

**権限による制御**：Java 9以降では`--add-opens`フラグによるモジュールアクセス許可が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに本機能に対応する画面定義なし |

## 機能種別

基盤 / メモリ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| object | Object | No | メモリ操作対象のオブジェクト（nullでオフヒープ） | - |
| offset | long | Yes | メモリオフセット | 有効なアドレス範囲 |
| size | long | Yes | メモリサイズ（バイト） | 正の値 |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 読み取り値 | int/long/float/double等 | メモリから読み取ったプリミティブ値 |
| MemoryBlock | MemoryBlock | 確保されたメモリブロック |
| ByteBuffer | DirectByteBuffer | 確保されたダイレクトバッファ |

## 処理フロー

### 処理シーケンス

```
1. Platform初期化（staticブロック）
   a. Unsafe._UNSAFEの取得（リフレクション経由）
   b. 各型の配列ベースオフセット計算（BYTE_ARRAY_OFFSET等）
   c. Unalignedアクセス可否判定
   d. DirectByteBuffer関連のコンストラクタ/フィールド/メソッドの取得
      - Java 21以降はLong型コンストラクタを使用
      - CLEANER_CREATE_METHODの可用性チェック

2. メモリ読み書き
   a. getInt/putInt等: _UNSAFE.getInt/putIntへの委譲
   b. getObjectVolatile/putObjectVolatile: volatile意味論でのオブジェクト操作

3. メモリ確保/解放
   a. allocateMemory: _UNSAFE.allocateMemoryへの委譲
   b. freeMemory: _UNSAFE.freeMemoryへの委譲
   c. reallocateMemory: 新メモリ確保→コピー→旧メモリ解放

4. メモリコピー（copyMemory）
   a. オーバーラップ検出: dstOffset < srcOffset の判定
   b. UNSAFE_COPY_THRESHOLD (1MB) 単位での分割コピー
   c. セーフポイントポーリングのための分割

5. DirectByteBuffer割り当て（allocateDirectBuffer）
   a. CLEANER_CREATE_METHOD利用不可の場合: ByteBuffer.allocateDirect()
   b. 利用可能な場合: allocateMemory → DBB_CONSTRUCTOR → Cleaner設定
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-93-01 | コピーしきい値 | copyMemoryは1MB単位で分割してセーフポイントポーリングを許可 | copyMemory呼び出し時 |
| BR-93-02 | オーバーラップ対応 | コピー元とコピー先が重複する場合、方向を判定して安全にコピー | src/dstが同一オブジェクト時 |
| BR-93-03 | JDK互換性 | Java 21以降はDirectByteBufferコンストラクタのシグネチャが変更 | majorVersion >= 21 |
| BR-93-04 | Cleaner可用性 | jdk.internal.ref.Cleanerが利用不可の場合は標準API経由で確保 | Java 9以降のモジュール制限 |
| BR-93-05 | ページ管理 | MemoryBlockのページ番号でTMM管理/解放済み/アロケータ解放済みを区別 | TaskMemoryManager使用時 |

### 計算ロジック

MemoryBlock.fromLongArray: `new MemoryBlock(array, Platform.LONG_ARRAY_OFFSET, array.length * 8L)` -- long配列のバイトサイズを計算してMemoryBlockを生成

## データベース操作仕様

本機能はデータベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| OutOfMemoryError | メモリ不足 | DirectByteBuffer割り当て失敗 | -XX:MaxDirectMemorySize を増加 |
| IllegalStateException | 初期化エラー | Unsafeインスタンス取得不可 | JVM互換性確認 |
| IllegalAccessException | アクセス制限 | jdk.internal.ref.Cleanerへのアクセス不可 | `--add-opens java.base/jdk.internal.ref=ALL-UNNAMED` フラグ追加 |

## トランザクション仕様

メモリ操作はトランザクション管理対象外。

## パフォーマンス要件

- ゼロコピー操作によるメモリ操作の高速化
- UNSAFE_COPY_THRESHOLD（1MB）によるセーフポイントポーリング確保
- Unaligned access対応によるアーキテクチャ最適化

## セキュリティ考慮事項

- sun.misc.Unsafeの使用によりJVMのメモリ安全性保証を回避する
- 不正なオフセット指定はJVMクラッシュの原因となりうる
- Java 9以降のモジュールシステムによるアクセス制御

## 備考

`common/unsafe/` パッケージ配下に実装。Sparkの性能を支える重要な低レベル基盤。

---

## コードリーディングガイド

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MemoryLocation.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/MemoryLocation.java` | メモリ位置の基底クラス。obj（ヒープ参照）とoffsetの組 |
| 1-2 | MemoryBlock.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/MemoryBlock.java` | **27-82行目**: MemoryLocation拡張。length、pageNumber管理。NO_PAGE_NUMBER(-1), FREED_IN_TMM_PAGE_NUMBER(-2), FREED_IN_ALLOCATOR_PAGE_NUMBER(-3)の特殊値 |

**読解のコツ**: MemoryBlockのobj=nullの場合はオフヒープメモリを表し、objが非nullの場合はヒープ上のオブジェクト（byte[]等）を表す。

#### Step 2: Platformクラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Platform.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/Platform.java` | 本機能の中核ファイル |

**主要処理フロー**:
- **30-50行目**: フィールド定義。_UNSAFE、各型のARRAY_OFFSET定数、unalignedフラグ
- **57-104行目**: staticブロック1。DirectByteBufferコンストラクタ・Cleanerの取得。Java 21分岐（64-66行目）
- **119-173行目**: プリミティブ型読み書き（getInt/putInt, getLong/putLong等）
- **183-196行目**: メモリ確保・解放・再確保。reallocateMemoryは新領域確保→コピー→旧領域解放
- **201-231行目**: allocateDirectBuffer。CLEANER_CREATE_METHODの可用性に応じた分岐
- **241-265行目**: copyMemory。オーバーラップ対応とUNSAFE_COPY_THRESHOLD分割
- **278行目**: UNSAFE_COPY_THRESHOLD = 1MB
- **280-338行目**: staticブロック2-3。_UNSAFE取得、配列オフセット計算、unaligned判定

#### Step 3: メモリアロケータを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | MemoryAllocator.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/MemoryAllocator.java` | メモリアロケータインターフェース |
| 3-2 | HeapMemoryAllocator.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/HeapMemoryAllocator.java` | ヒープメモリアロケータ |
| 3-3 | UnsafeMemoryAllocator.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/UnsafeMemoryAllocator.java` | オフヒープメモリアロケータ |

### プログラム呼び出し階層図

```
Platform (staticメソッド群)
    |
    +-- getInt/putInt, getLong/putLong, ... [各型アクセス]
    |     +-- _UNSAFE.getInt/putInt, ...
    |
    +-- allocateMemory(size)
    |     +-- _UNSAFE.allocateMemory(size)
    |
    +-- freeMemory(address)
    |     +-- _UNSAFE.freeMemory(address)
    |
    +-- reallocateMemory(address, oldSize, newSize)
    |     +-- allocateMemory(newSize)
    |     +-- copyMemory(old, new, oldSize)
    |     +-- freeMemory(address)
    |
    +-- copyMemory(src, srcOff, dst, dstOff, length)
    |     +-- [オーバーラップ判定]
    |     +-- _UNSAFE.copyMemory() [UNSAFE_COPY_THRESHOLD単位]
    |
    +-- allocateDirectBuffer(size)
          +-- [CLEANER_CREATE_METHOD可用性チェック]
          +-- allocateMemory(size) [バイパス経路]
          +-- DBB_CONSTRUCTOR.newInstance()
          +-- CLEANER_CREATE_METHOD.invoke()
```

### データフロー図

```
[呼び出し元]                     [Platform]                     [sun.misc.Unsafe]

UnsafeRow/TungstenSort  ──>   getInt/putInt(obj,off)    ──>   _UNSAFE.getInt/putInt
TaskMemoryManager       ──>   allocateMemory(size)      ──>   _UNSAFE.allocateMemory
                        ──>   freeMemory(addr)          ──>   _UNSAFE.freeMemory
Tungsten Sort           ──>   copyMemory(src,dst,len)   ──>   _UNSAFE.copyMemory [分割]
ByteBuffer Management   ──>   allocateDirectBuffer(sz)  ──>   DBB_CONSTRUCTOR / ByteBuffer.allocateDirect
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Platform.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/Platform.java` | ソース | sun.misc.Unsafeの安全ラッパー |
| MemoryBlock.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/MemoryBlock.java` | ソース | メモリブロック管理 |
| MemoryLocation.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/MemoryLocation.java` | ソース | メモリ位置の基底クラス |
| MemoryAllocator.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/MemoryAllocator.java` | ソース | メモリアロケータインターフェース |
| HeapMemoryAllocator.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/HeapMemoryAllocator.java` | ソース | ヒープメモリアロケータ |
| UnsafeMemoryAllocator.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/memory/UnsafeMemoryAllocator.java` | ソース | オフヒープメモリアロケータ |
| BitSet.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/bitset/BitSet.java` | ソース | ビットセット操作 |
| UTF8String.java | `common/unsafe/src/main/java/org/apache/spark/unsafe/types/UTF8String.java` | ソース | UTF-8文字列のUnsafe操作 |
