static变量到底存在什么位置

2017-01-16  本文已影响0人  风干鸡

static变量到底存在什么位置

上一篇里面提到了一些关于static变量位置的猜测,现在就来验证一下。

要验证的主要有以下几点:

环境

$ java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

为了内存比较“整齐”,关闭压缩指针,启动参数加上-XX:-UseCompressedOops

前期准备

jol打印Class类信息,版本 :

<dependencies>
    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.6</version>
    </dependency>
</dependencies>
import org.openjdk.jol.info.ClassLayout;

public class Test {
    public static void main(String[] args){
        System.out.println(ClassLayout.parseClass(java.lang.Class.class).toPrintable());
    }
}

输出:

# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
java.lang.Class object internals:
 OFFSET  SIZE            TYPE DESCRIPTION                    VALUE
      0    16                 (object header)                N/A
     16     8     Constructor Class.cachedConstructor        N/A
     24     8           Class Class.newInstanceCallerCache   N/A
     32     8          String Class.name                     N/A
     40     8                 (alignment/padding gap)        N/A
     48     8   SoftReference Class.reflectionData           N/A
     56     8 ClassRepository Class.genericInfo              N/A
     64     8        Object[] Class.enumConstants            N/A
     72     8             Map Class.enumConstantDirectory    N/A
     80     8  AnnotationData Class.annotationData           N/A
     88     8  AnnotationType Class.annotationType           N/A
     96     8   ClassValueMap Class.classValueMap            N/A
    104    40                 (alignment/padding gap)        N/A
    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

Class对象大小为152,那么instanceKlass指针的地址应该在Class对象+152偏移量上,静态变量在160偏移量上。

测试代码:

public class Test {
    static long static_field = 0x111;
    void fun() {
        //断点打在这里
        System.out.println("hehe"); 
    }
    public static void main(String[] args){
        new Test().fun();
    }
}

断点打在fun方法的这里,开启调试模式。

查看进程id:

$ jps
1792
6161 Jps
1913 RemoteMavenServer
6123 Launcher
6126 Test

打开HSDB:

sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

连接到6126上:

Screen Shot 2017-01-16 at 12.16.22.png

在命令行里面输入universe,输出:

hsdb> universe
Heap Parameters:
ParallelScavengeHeap [ PSYoungGen [ eden =  [0x00000001cdb00000,0x00000001ce01ecc8,0x00000001d1b00000] , from =  [0x00000001d2580000,0x00000001d2580000,0x00000001d3000000] , to =  [0x00000001d1b00000,0x00000001d1b00000,0x00000001d2580000]  ] PSOldGen [  [0x0000000123000000,0x0000000123000000,0x000000012db00000]  ]  ]

可以看到YoungGen地址范围是0x00000001cdb00000 - 0x00000001d3000000,而且YoungGen和OldGen并不连续,在这里面扫描Test对象。

hsdb> scanoops 0x00000001cdb00000 0x00000001d3000000 Test
0x00000001cdbfce80 Test

Test类没有实例变量(instance variable),所以他的大小是16字节,mark+klass指针(64位关闭压缩指针的情况下)。

hsdb> mem 0x00000001cdbfce80 2
0x00000001cdbfce80: 0x0000000000000001 
0x00000001cdbfce88: 0x0000000111d174e0 

在Inspector中输入0x0000000111d174e0:

Screen Shot 2017-01-16 at 12.37.19.png

可以看到java mirror(也就是Class对应的java 对象)地址为:0x00000001cdbfc418

上面static_field已经把静态变量显示出来了,用jshell把0x111打印出来:

jshell> 0x111
$1 ==> 273

前面已经提到当前环境下Class对象的大小为152,也就是19个字长,再加上我们要查看的instanceKlass指针和long变量,一共是21。

hsdb> mem 0x00000001cdbfc418 21
0x00000001cdbfc418: 0x000000762efe5d01 
0x00000001cdbfc420: 0x000000011192b288 
0x00000001cdbfc428: 0x0000000000000000 
0x00000001cdbfc430: 0x0000000000000000 
0x00000001cdbfc438: 0x00000001cdbfce28 
0x00000001cdbfc440: 0x00000001cdb7b1a8 
0x00000001cdbfc448: 0x00000001cdbfcb10 
0x00000001cdbfc450: 0x0000000000000000 
0x00000001cdbfc458: 0x0000000000000000 
0x00000001cdbfc460: 0x0000000000000000 
0x00000001cdbfc468: 0x0000000000000000 
0x00000001cdbfc470: 0x0000000000000000 
0x00000001cdbfc478: 0x0000000000000000 
0x00000001cdbfc480: 0x00000001cdbfbdf0 
0x00000001cdbfc488: 0x0000000000000000 
0x00000001cdbfc490: 0x0000000000000000 
0x00000001cdbfc498: 0x0000000111d174e0 
0x00000001cdbfc4a0: 0x0000000000000000 
0x00000001cdbfc4a8: 0x0000001500000000 
0x00000001cdbfc4b0: 0x0000000000000000 
0x00000001cdbfc4b8: 0x0000000000000111 

可以看到klass指针并不在class对象的末尾,而是在偏移量17*8上(有点尴尬)。根据上面打印的Class信息,这里是alignment/padding gap。我算了一下,这个空白应该不是补齐,因为开启压缩指针的情况下class对象大小为96,字长12已经很齐了,而class对象尾部的空白是做什么的还有待考证。

java_mirror地址:0x00000001cdbfc418,YoungGen地址范围是0x00000001cdb00000 - 0x00000001d3000000。

jshell> 0x00000001cdb00000l < 0x00000001cdbfc418l && 0x00000001cdbfc418l < 0x00000001d3000000
$10 ==> true

class对象确实在堆里。

总结:

上一篇下一篇

猜你喜欢

热点阅读