为什么同一个静态字段在 dalvik 中有两个不同的 id?
How come the same static field has two different ids in dalvik?
我正在编写自己的玩具 Dalvik VM,我似乎无法弄清楚 dalvik 如何处理继承的静态字段。
考虑以下 java 代码:
class Parent { static int parent_int = 10; }
class MyCode extends Parent {
public static void main(String[] args){
System.out.println(parent_int + 1);
}
}
当使用 javac 和 java 编译并 运行 时,它会像预期的那样将 11
打印到控制台。但是,当它被编译为 dalvik 时,parent_int
值被转换为 sget
语句以获得 field@0000
,而在 Parent
的 <clinit>
方法中parent_int
的字段 ID 是 field@0001
.
在我的 Dalvik VM 实现中,这成为一个问题,因为 field@0000
没有初始化,即使 Parent
class 和 field@0001
已经初始化。
Dalvik VM 如何处理这个问题?它怎么知道它们是相关的,并且应该被认为是相同的?为什么一开始它们就变成了两个不同的领域,而它们本来也可以成为一个领域?
简答:field@0000 和 field@0001 实际上是不同的引用。
更长:让我们一步一步来:
反编译:
[tmp]$ dextra -j -d -D classes.dex
/* 0 */ class Parent {
/** 1 Static Fields (not printed - use -f to print) **/
/** 2 Direct Methods **/
static void <clinit> () // Class Constructor
{
/* # of Registers: 1 */
/* # of Instructions: 5 */
/* 0000: Op 1300 0a00 const/16 v0, 0xa */
v0 = 10;
/* 0002: Op 6700 0100 sput v0, ?@1 */
Parent.parent_int = v0; // (Field@1)
/* 0004: Op 0e00 return-void */
return;
} // end <clinit>
void <init> () // Constructor
{
/* # of Registers: 1 */
/* # of Instructions: 4 */
/* 0000: Op 7010 0500 0000 invoke-direct { v0 } */
result = java.lang.Object.<init>(v0); // (Method@5(v0))
/* 0003: Op 0e00 return-void */
return;
} // end <init>
} // end class Parent
/* 1 */ class MyCode extends Parent { /** 2 Direct Methods **/
void <init> () // Constructor
{
/* # of Registers: 1 */
/* # of Instructions: 4 */
/* 0000: Op 7010 0300 0000 invoke-direct { v0 } */
result = Parent.<init>(v0); // (Method@3(v0))
/* 0003: Op 0e00 return-void */
return;
} // end <init>
public static void main (java.lang.String[])
{
/* # of Registers: 2 */
/* # of Instructions: 19 */
/* 0000: Op 6201 0200 sget-object v1, ?@2 */
v1 = java.lang.System.out; // (Field@2)
/* 0002: Op 6000 0000 sget v0, ?@0 */
v0 = MyCode.parent_int; // (Field@0)
/* 0004: Op d800 0001 add-int/lit8 v0, 1 */
v0 += 1;
/* 0006: Op 6e20 0400 0100 invoke-virtual { v1, v0 } */
result = java.io.PrintStream.println(java.lang.System.out, MyCode.parent_int); // (Method@4(v1, v0))
/* 0009: Op 6201 0200 sget-object v1, ?@2 */
v1 = java.lang.System.out; // (Field@2)
/* 000b: Op 6000 0100 sget v0, ?@1 */
v0 = Parent.parent_int; // (Field@1)
/* 000d: Op d800 0001 add-int/lit8 v0, 1 */
v0 += 1;
/* 000f: Op 6e20 0400 0100 invoke-virtual { v1, v0 } */
result = java.io.PrintStream.println(java.lang.System.out, Parent.parent_int); // (Method@4(v1, v0))
/* 0012: Op 0e00 return-void */
return;
} // end main
} // end class MyCode
(这是您的代码,但在您的代码之后添加另一个“System.out.println(Parent.parent_int + 1);”)
现在,正如您所说的那样,父项引用 field1,而主项查看 @0。如果我们显示字段:
[tmp]$ dextra -F classes.dex
Field (0) Definer: LMyCode; Name: parent_int type: I Flags: 0x0
Field (1) Definer: LParent; Name: parent_int type: I Flags: 0x0
Field (2) Definer: Ljava/lang/System; Name: out type: Ljava/io/PrintStream; Flags: 0x0
我们看到字段1属于Parent,字段0属于MyCode。由于它们都是静态的,它们分别由每个 class 拥有:在 Dalvik 对 MyCode 的内部初始化时(我们看不到,因为它是在运行时级别处理的,现在通过从 .ART 克隆影子) ,字段引用“static_int”被复制(从子项的影子,而不是父项)——但对父项字段的进一步引用(如我添加的 system.out.println)将解决字段 1 , 不是 0.
编辑:请注意,从 class 的角度来看,只有字段属于父级:
Class 0:父文件:MyCode.java
1个静态字段
2 直接法
Class 1: 我的代码
扩展 LParent;文件:MyCode.java
2 种直接方法
因此“parent_int”在很大程度上归父class所有。
请注意,您总是在这个或那个 object/class 的上下文中引用该字段,所以如果不是为了那个优化。所以 - 严格来说,Dalvik 可以折叠两个字段(相同) - 但不会这样做,因为拥有这个看似“重复”字段的成本并不高,值得快速访问定义器。
我正在编写自己的玩具 Dalvik VM,我似乎无法弄清楚 dalvik 如何处理继承的静态字段。
考虑以下 java 代码:
class Parent { static int parent_int = 10; }
class MyCode extends Parent {
public static void main(String[] args){
System.out.println(parent_int + 1);
}
}
当使用 javac 和 java 编译并 运行 时,它会像预期的那样将 11
打印到控制台。但是,当它被编译为 dalvik 时,parent_int
值被转换为 sget
语句以获得 field@0000
,而在 Parent
的 <clinit>
方法中parent_int
的字段 ID 是 field@0001
.
在我的 Dalvik VM 实现中,这成为一个问题,因为 field@0000
没有初始化,即使 Parent
class 和 field@0001
已经初始化。
Dalvik VM 如何处理这个问题?它怎么知道它们是相关的,并且应该被认为是相同的?为什么一开始它们就变成了两个不同的领域,而它们本来也可以成为一个领域?
简答:field@0000 和 field@0001 实际上是不同的引用。
更长:让我们一步一步来:
反编译:
[tmp]$ dextra -j -d -D classes.dex
/* 0 */ class Parent {
/** 1 Static Fields (not printed - use -f to print) **/
/** 2 Direct Methods **/
static void <clinit> () // Class Constructor
{
/* # of Registers: 1 */
/* # of Instructions: 5 */
/* 0000: Op 1300 0a00 const/16 v0, 0xa */
v0 = 10;
/* 0002: Op 6700 0100 sput v0, ?@1 */
Parent.parent_int = v0; // (Field@1)
/* 0004: Op 0e00 return-void */
return;
} // end <clinit>
void <init> () // Constructor
{
/* # of Registers: 1 */
/* # of Instructions: 4 */
/* 0000: Op 7010 0500 0000 invoke-direct { v0 } */
result = java.lang.Object.<init>(v0); // (Method@5(v0))
/* 0003: Op 0e00 return-void */
return;
} // end <init>
} // end class Parent
/* 1 */ class MyCode extends Parent { /** 2 Direct Methods **/
void <init> () // Constructor
{
/* # of Registers: 1 */
/* # of Instructions: 4 */
/* 0000: Op 7010 0300 0000 invoke-direct { v0 } */
result = Parent.<init>(v0); // (Method@3(v0))
/* 0003: Op 0e00 return-void */
return;
} // end <init>
public static void main (java.lang.String[])
{
/* # of Registers: 2 */
/* # of Instructions: 19 */
/* 0000: Op 6201 0200 sget-object v1, ?@2 */
v1 = java.lang.System.out; // (Field@2)
/* 0002: Op 6000 0000 sget v0, ?@0 */
v0 = MyCode.parent_int; // (Field@0)
/* 0004: Op d800 0001 add-int/lit8 v0, 1 */
v0 += 1;
/* 0006: Op 6e20 0400 0100 invoke-virtual { v1, v0 } */
result = java.io.PrintStream.println(java.lang.System.out, MyCode.parent_int); // (Method@4(v1, v0))
/* 0009: Op 6201 0200 sget-object v1, ?@2 */
v1 = java.lang.System.out; // (Field@2)
/* 000b: Op 6000 0100 sget v0, ?@1 */
v0 = Parent.parent_int; // (Field@1)
/* 000d: Op d800 0001 add-int/lit8 v0, 1 */
v0 += 1;
/* 000f: Op 6e20 0400 0100 invoke-virtual { v1, v0 } */
result = java.io.PrintStream.println(java.lang.System.out, Parent.parent_int); // (Method@4(v1, v0))
/* 0012: Op 0e00 return-void */
return;
} // end main
} // end class MyCode
(这是您的代码,但在您的代码之后添加另一个“System.out.println(Parent.parent_int + 1);”)
现在,正如您所说的那样,父项引用 field1,而主项查看 @0。如果我们显示字段:
[tmp]$ dextra -F classes.dex
Field (0) Definer: LMyCode; Name: parent_int type: I Flags: 0x0
Field (1) Definer: LParent; Name: parent_int type: I Flags: 0x0
Field (2) Definer: Ljava/lang/System; Name: out type: Ljava/io/PrintStream; Flags: 0x0
我们看到字段1属于Parent,字段0属于MyCode。由于它们都是静态的,它们分别由每个 class 拥有:在 Dalvik 对 MyCode 的内部初始化时(我们看不到,因为它是在运行时级别处理的,现在通过从 .ART 克隆影子) ,字段引用“static_int”被复制(从子项的影子,而不是父项)——但对父项字段的进一步引用(如我添加的 system.out.println)将解决字段 1 , 不是 0.
编辑:请注意,从 class 的角度来看,只有字段属于父级:
Class 0:父文件:MyCode.java 1个静态字段 2 直接法 Class 1: 我的代码 扩展 LParent;文件:MyCode.java 2 种直接方法
因此“parent_int”在很大程度上归父class所有。
请注意,您总是在这个或那个 object/class 的上下文中引用该字段,所以如果不是为了那个优化。所以 - 严格来说,Dalvik 可以折叠两个字段(相同) - 但不会这样做,因为拥有这个看似“重复”字段的成本并不高,值得快速访问定义器。