Java static變數儲存在哪?
測試環境:
Microsoft Windows [版本 10.0.17134.165]
java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
測試程式碼:
import java.io.IOException;
public class Main {
private static String name = "lgh";
private static int age = 26;
public int fun() {
try {
System.out.println(name);
System.out.println(age);
return System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
public static void main (String[] args) {
new Main().fun();
}
}
編譯&執行:
D:\N3verL4nd\Desktop>javac Main.java
D:\N3verL4nd\Desktop>java -XX:+UseSerialGC -XX:-UseCompressedOops -Xms10m -Xmx10m Main
lgh
26
System.in.read()
的作用等同於斷點。
使用 CLHSDB 連線:
// 檢視程序 id
D:\>jps
5792 Jps
7932 Main
D:\>java -cp .;%JAVA_HOME%/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
hsdb> attach 7932
Attaching to process 7932, please wait...
執行 universe
:
Heap Parameters:
Gen 0: eden [0x0000000012600000,0x00000000127114d0,0x00000000128b0000) space capacity = 2818048, 39.7239507630814 used
from [0x00000000128b0000,0x00000000128b0000,0x0000000012900000) space capacity = 327680, 0.0 used
to [0x0000000012900000,0x0000000012900000,0x0000000012950000) space capacity = 327680, 0.0 usedInvocations: 0
Gen 1: old [0x0000000012950000,0x0000000012950000,0x0000000013000000) space capacity = 7012352, 0.0 usedInvocations: 0
[eden] 0x00000000128b0000 - 0x0000000012600000 = 2B 0000(1260 0000)
[from] 0x0000000012900000 - 0x00000000128b0000 = 5 0000(120 0000)
[to] 0x0000000012950000 - 0x0000000012900000 = 5 0000(120 0000)
可以看到 eden:from:to 大致比例為8:1:1,可以看到新生代的[eden-from-to]記憶體是連續的。同時可以看新生代和老年代記憶體是連著的。大概和垃圾回收方式有關。
掃描我們的 Main 例項:
hsdb> scanoops 0x0000000012600000 0x00000000128b0000 Main
0x000000001270afd8 Main
hsdb> whatis 0x000000001270afd8
Address 0x000000001270afd8: In thread-local allocation buffer for thread "main" (1) [0x0000000012703870,0x000000001270b6e8,0x00000000127114b8,{0x00000000127114d0})
hsdb> inspect 0x000000001270afd8
instance of Oop for Main @ 0x000000001270afd8 @ 0x000000001270afd8 (size = 16)
_mark: 1
_metadata._klass: InstanceKlass for Main
hsdb>
可見,Main 例項分配在了執行緒私有的 TLAB 中。
Main 類沒有例項變數,所以他的大小是 16 位元組,Mark Word + Klass 指標(64 位 JVM 關閉壓縮指標的情況下)。
使用 inspect
命令沒有顯示出來 InstanceKlass 也就是型別指標的地址,據說是 HSDB 的bug。我們使用 mem
來獲取更詳細的資訊。
hsdb> mem 0x000000001270afd8 2
0x000000001270afd8: 0x0000000000000001 // Mark Word
0x000000001270afe0: 0x0000000013400598 // 型別指標(與Mark Word 一起組成物件頭)
由於 1 個十六進位制位代表 4 個二進位制位,所以以上 Mark Word 的最後一位 1 代表的二進位制序列為0001。
也就是 Main 例項處在無鎖狀態。
檢視該型別指標對應的資料:
hsdb> inspect 0x0000000013400598
Type is InstanceKlass (size of 440)
juint Klass::_super_check_offset: 48
Klass* Klass::_secondary_super_cache: Klass @ null
Array<Klass*>* Klass::_secondary_supers: Array<Klass*> @ 0x0000000013000f88
Klass* Klass::_primary_supers[0]: Klass @ 0x0000000013001c00
oop Klass::_java_mirror: Oop for java/lang/Class @ 0x0000000012709dc8 Oop for java/lang/Class @ 0x0000000012709dc8
或者使用 HSDB :
D:\Java\Tools\jol>java -XX:-UseCompressedOops -jar jol-cli.jar internals java.lang.Class
# Running 64-bit HotSpot VM.
# Objects are 8 bytes aligned.
# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Failed to find matching constructor, falling back to class-only introspection.
java.lang.Class object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 16 (object header) N/A
16 8 java.lang.reflect.Constructor Class.cachedConstructor N/A
24 8 java.lang.Class Class.newInstanceCallerCache N/A
32 8 java.lang.String Class.name N/A
40 8 (alignment/padding gap)
48 8 java.lang.ref.SoftReference Class.reflectionData N/A
56 8 sun.reflect.generics.repository.ClassRepository Class.genericInfo N/A
64 8 java.lang.Object[] Class.enumConstants N/A
72 8 java.util.Map Class.enumConstantDirectory N/A
80 8 java.lang.Class.AnnotationData Class.annotationData N/A
88 8 sun.reflect.annotation.AnnotationType Class.annotationType N/A
96 8 java.lang.ClassValue.ClassValueMap Class.classValueMap N/A
104 40 (alignment/padding gap)
144 4 int Class.classRedefinedCount N/A
148 4 (loss due to the next object alignment)
Instance size: 152 bytes
Space losses: 48 bytes internal + 4 bytes external = 52 bytes total
使用 jol 獲得 Class 物件的大小為 152,也就是 19 個字長。
hsdb> inspect 0x0000000012709dc8
instance of Oop for java/lang/Class @ 0x0000000012709dc8 @ 0x0000000012709dc8 (size = 176)
name: "lgh" @ 0x000000001270af80 Oop for java/lang/String @ 0x000000001270af80
age: 26
hsdb> mem 0x0000000012709dc8 22
0x0000000012709dc8: 0x0000002a139a5501 // 1
0x0000000012709dd0: 0x0000000013013ed0 // 2
0x0000000012709dd8: 0x0000000000000000 // 3
0x0000000012709de0: 0x0000000000000000 // 4
0x0000000012709de8: 0x0000000000000000 // 5
0x0000000012709df0: 0x00000000126e5348 // 6
0x0000000012709df8: 0x000000001270a4c8 // 7
0x0000000012709e00: 0x0000000000000000 // 8
0x0000000012709e08: 0x0000000000000000 // 9
0x0000000012709e10: 0x0000000000000000 // 10
0x0000000012709e18: 0x0000000000000000 // 11
0x0000000012709e20: 0x0000000000000000 // 12
0x0000000012709e28: 0x0000000000000000 // 13
0x0000000012709e30: 0x00000000127097d0 // 14
0x0000000012709e38: 0x0000000000000000 // 15
0x0000000012709e40: 0x0000000000000000 // 16
0x0000000012709e48: 0x0000000013400598 // 17 型別指標
0x0000000012709e50: 0x0000000000000000 // 18
0x0000000012709e58: 0x0000001600000000 // 19
0x0000000012709e60: 0x0000000000000001 // 20
0x0000000012709e68: 0x000000001270af80 // 21 "lgh" 的引用
0x0000000012709e70: 0x000000000000001a // 22 "26" 的 16 進製表示
可以看到 static 變數儲存在 Class 例項的尾部。
Class 物件確實在堆中。
型別指標儲存在 Class 例項 17 * 8 的位置上。