常量池是 Class 文件中最複雜、體積最大、且運行時訪問最頻繁的區域。KVM 通過 Union 統一大小、類型標識後置數組、定點數索引 三大技巧,在極受限內存下實現了高效、緊湊的常量池運行時表示。
| 常量類型 | Tag 值 | 含義 |
|---|---|---|
| CONSTANT_Utf8 | 1 | UTF-8 編碼字符串 (類名、方法名、字面量) |
| CONSTANT_Integer | 3 | int 常量 |
| CONSTANT_Float | 4 | float 常量 |
| CONSTANT_Long | 5 | long 常量 (佔 2 槽) |
| CONSTANT_Double | 6 | double 常量 (佔 2 槽) |
| CONSTANT_Class | 7 | 類/接口符號引用 |
| CONSTANT_String | 8 | String 字面量引用 |
| CONSTANT_Fieldref | 9 | 字段符號引用 |
| CONSTANT_Methodref | 10 | 類方法符號引用 |
| CONSTANT_InterfaceMethodref | 11 | 接口方法符號引用 |
| CONSTANT_NameAndType | 12 | 名稱+描述符符號引用 |
union constantPoolEntryStruct {
struct { // Fieldref/Methodref/InterfaceMethodref 共用
unsigned short classIndex;
unsigned short nameTypeIndex;
} method;
CLASS clazz; // CONSTANT_Class 解析後指針
INTERNED_STRING_INSTANCE String; // CONSTANT_String 解析後對象
cell* cache; // 通用緩存指針 (clazz 或 String)
cell integer; // Integer/Float 原始位模式
long length; // Long/Double 高 32 位 (低位在下一項)
NameTypeKey nameTypeKey; // NameAndType 緊湊鍵
NameKey nameKey; // 字段/方法名緊湊鍵
UString ustring; // Utf8 字符串對象
};
Union 確保每項固定大小 (4 字節/字),可構成數組 entries[] 實現 O(1) 索引訪問。
struct constantPoolStruct {
union constantPoolEntryStruct entries[1]; // 柔性數組
};
巧妙點:數組第 0 項 不存儲常量,而是存儲 constant_pool_count (即實際項數 + 1)。這與 Class 文件格式中 constant_pool_count 緊跟在計數器之後、且索引從 1 開始的規約完美對應。訪問第 i 項常量即 pool.entries[i],長度為 pool.entries[0].integer。
Union 不攜帶類型信息。KVM 在 entries[] 數組末尾緊跟一個字節數組 tags[],長度 = constant_pool_count - 1,tags[i-1] 對應第 i 項的 Tag 值。實現「數據與類型分離、緊湊佈局」。
位於 kvm/vmcommon/src/loader.c,三階段:
RawPool (保留文件原始布局)
int numberOfEntries = rawPool.count; // 實際項數
// entries[] 大小 (以 union 為單位) + tags[] 向 4 字節對齊
int tableSize = numberOfEntries + ((numberOfEntries + 3) >> 2);
CONSTANTPOOL pool = (CONSTANTPOOL) malloc(tableSize * sizeof(union constantPoolEntryStruct));
pool.entries[0].integer = numberOfEntries + 1; // 存入 count
byte* tags = (byte*)&pool.entries[tableSize - (numberOfEntries + 3) / 4]; // 定位 tags 區
((n + 3) >> 2) 即 ceil(n/4),將 tags 字節數組大小換算為 union 元素個數,實現 4 字節內存對齊分配。
RawPool,將 Utf8 轉為 UString、Class 引用解析為 CLASS*、String 引用解析為 INTERNED_STRING_INSTANCE 等pool.entries[i] 對應 union 字段tags[i-1] = rawTagpool 掛載至 CurrentClass.constPool| 設計決策 | 優勢 | 代價 |
|---|---|---|
| Union 統一項大小 | O(1) 索引、無內存碎片、緊湊佈局 | 空間浪費:小項 (如 Integer) 佔用大項 (如 Methodref) 空間 |
| 數組第 0 項存 count | 與 Class 文件索引規約零成本映射 | 語義混淆:數組首項非數據項,需文檔約定 |
| Tags 後置字節數組 | 數據區純 Union 可直接按類型強轉、緊湊對齊 | 訪問類型需兩次內存訪問 (entries + tags)、稍增代碼複雜度 |
| 流式單遍構建 + 回填 | 無需雙遍掃描、臨時內存可控 | 前向引用需佔位符機制 (RawPool 暫存索引) |