C++でアドレスを渡す場合、constをつけている。
同じ感覚でJavaでもfinalを書いていたのだが、AndroidのAPIではつけているものを見かけない。
ネットで調べると、侃々諤々というか、不毛というか、よくわからんごとなってきた。
ちょっとだけやってみよう。
Oracleのサーバ向けのドキュメントでは、チューニングガイドにちょっと載っていた。
http://download.oracle.com/docs/cd/E19528-01/820-1613/abebm/index.html
1: package com.blogpost.hiro99ma;
2:
3: public class FinalTest {
4: static void print(byte[] b) {
5: for(int i=0; i<b.length; i++) {
6: System.out.print(b[i]);
7: }
8: System.out.println();
9:
10: b[0] = 5;
11: }
12:
13: public static void main(String[] args) {
14: byte[] a = new byte[] { 1, 2, 3 };
15: print(a);
16:
17: for(int i=0; i<a.length; i++) {
18: System.out.print(a[i]);
19: }
20: System.out.println();
21: }
22: }
まあ、シンプルに。
実行すると、こうなる。
123
523
予想通りだ。
classファイルは846byte。
バイトコードは、こうなってる。
// Compiled from FinalTest.java (version 1.6 : 50.0, super bit)
public class com.blogpost.hiro99ma.FinalTest {
// Method descriptor #6 ()V
// Stack: 1, Locals: 1
public FinalTest();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8]
4 return
Line numbers:
[pc: 0, line: 3]
Local variable table:
[pc: 0, pc: 5] local: this index: 0 type: com.blogpost.hiro99ma.FinalTest
// Method descriptor #15 ([B)V
// Stack: 3, Locals: 2
static void print(byte[] b);
0 iconst_0
1 istore_1 [i]
2 goto 17
5 getstatic java.lang.System.out : java.io.PrintStream [16]
8 aload_0 [b]
9 iload_1 [i]
10 baload
11 invokevirtual java.io.PrintStream.print(int) : void [22]
14 iinc 1 1 [i]
17 iload_1 [i]
18 aload_0 [b]
19 arraylength
20 if_icmplt 5
23 getstatic java.lang.System.out : java.io.PrintStream [16]
26 invokevirtual java.io.PrintStream.println() : void [27]
29 aload_0 [b]
30 iconst_0
31 iconst_5
32 bastore
33 return
Line numbers:
[pc: 0, line: 5]
[pc: 5, line: 6]
[pc: 14, line: 5]
[pc: 23, line: 8]
[pc: 29, line: 10]
[pc: 33, line: 11]
Local variable table:
[pc: 0, pc: 34] local: b index: 0 type: byte[]
[pc: 2, pc: 23] local: i index: 1 type: int
Stack map table: number of frames 2
[pc: 5, append: {int}]
[pc: 17, same]
// Method descriptor #36 ([Ljava/lang/String;)V
// Stack: 4, Locals: 3
public static void main(java.lang.String[] args);
0 iconst_3
1 newarray byte [8]
3 dup
4 iconst_0
5 iconst_1
6 bastore
7 dup
8 iconst_1
9 iconst_2
10 bastore
11 dup
12 iconst_2
13 iconst_3
14 bastore
15 astore_1 [a]
16 aload_1 [a]
17 invokestatic com.blogpost.hiro99ma.FinalTest.print(byte[]) : void [37]
20 iconst_0
21 istore_2 [i]
22 goto 37
25 getstatic java.lang.System.out : java.io.PrintStream [16]
28 aload_1 [a]
29 iload_2 [i]
30 baload
31 invokevirtual java.io.PrintStream.print(int) : void [22]
34 iinc 2 1 [i]
37 iload_2 [i]
38 aload_1 [a]
39 arraylength
40 if_icmplt 25
43 getstatic java.lang.System.out : java.io.PrintStream [16]
46 invokevirtual java.io.PrintStream.println() : void [27]
49 return
Line numbers:
[pc: 0, line: 14]
[pc: 16, line: 15]
[pc: 20, line: 17]
[pc: 25, line: 18]
[pc: 34, line: 17]
[pc: 43, line: 20]
[pc: 49, line: 21]
Local variable table:
[pc: 0, pc: 50] local: args index: 0 type: java.lang.String[]
[pc: 16, pc: 50] local: a index: 1 type: byte[]
[pc: 22, pc: 43] local: i index: 2 type: int
Stack map table: number of frames 2
[pc: 25, append: {byte[], int}]
[pc: 37, same]
}
お、と思ったかもしれないが、バイトコードは読んだことはない。
まあ、アセンブラも適当に読めるものだから、なんとかなるだろう。
Method #6は、デフォルトコンストラクタだろう。
Method #15は、print()。aloadはarrayでiloadはintか?
「b[0]=5」は29~31行でやっているようだ。
「b[2]=9」にすると、ちょっと変わった。
29 aload_0 [b]
30 iconst_2
31 bipush 9
うーん、iconstからbipushになったな・・・。
えーい、これならどうだ!
b[0] = 0;
b[0] = 1;
b[0] = 2;
b[0] = 3;
b[0] = 4;
b[0] = 5;
b[0] = 6;
結果は・・・
29 aload_0 [b]
30 iconst_0
31 iconst_0
32 bastore
33 aload_0 [b]
34 iconst_0
35 iconst_1
36 bastore
37 aload_0 [b]
38 iconst_0
39 iconst_2
40 bastore
41 aload_0 [b]
42 iconst_0
43 iconst_3
44 bastore
45 aload_0 [b]
46 iconst_0
47 iconst_4
48 bastore
49 aload_0 [b]
50 iconst_0
51 iconst_5
52 bastore
53 aload_0 [b]
54 iconst_0
55 bipush 6
57 bastore
2つわかった。
iconstとipushが切り替わる境目は、代入する値が5以下か6以上かであること。
もう1つは、bastoreは毎回呼ばれるということ。
bはbyteのbだろう。intにして確認。
29 aload_0 [b]
30 iconst_0
31 iconst_0
32 iastore
33 aload_0 [b]
34 iconst_0
35 iconst_1
36 iastore
37 aload_0 [b]
38 iconst_0
39 iconst_2
40 iastore
41 aload_0 [b]
42 iconst_0
43 iconst_3
44 iastore
45 aload_0 [b]
46 iconst_0
47 iconst_4
48 iastore
49 aload_0 [b]
50 iconst_0
51 iconst_5
52 iastore
53 aload_0 [b]
54 iconst_0
55 bipush 6
57 iastore
「aload_0 [b]」で、引数の先頭アドレスを取ってきて、その次の「iconst_x」でオフセットを設定。
その次の「iconst_y」で代入する値を決めて、その次の「iastore」で代入、というところか。
違ってたら済まん。
驚いたのは、b[0]に代入するだけのコードを立て続けに書いたのに、最適化してないこと。
まるでvolatile変数みたいだ。
デフォルトだとこうなるのかしら。
最初に戻って。
これの引数にfinalをつけてみよう。
1: package com.blogpost.hiro99ma;
2:
3: public class FinalTest {
4: static void print(final byte[] b) {
5: for(int i=0; i<b.length; i++) {
6: System.out.print(b[i]);
7: }
8: System.out.println();
9:
10: b[0] = 5;
11: }
12:
13: public static void main(String[] args) {
14: byte[] a = new byte[] { 1, 2, 3 };
15: print(a);
16:
17: for(int i=0; i<a.length; i++) {
18: System.out.print(a[i]);
19: }
20: System.out.println();
21: }
22: }
実行結果は同じ。
123
523
finalでは指している先の変更は妨げることができないというのは、わかった。
では、バイトコードに違いはあるのだろうか。
// Compiled from FinalTest.java (version 1.6 : 50.0, super bit)
public class com.blogpost.hiro99ma.FinalTest {
// Method descriptor #6 ()V
// Stack: 1, Locals: 1
public FinalTest();
0 aload_0 [this]
1 invokespecial java.lang.Object() [8]
4 return
Line numbers:
[pc: 0, line: 3]
Local variable table:
[pc: 0, pc: 5] local: this index: 0 type: com.blogpost.hiro99ma.FinalTest
// Method descriptor #15 ([B)V
// Stack: 3, Locals: 2
static void print(byte[] b);
0 iconst_0
1 istore_1 [i]
2 goto 17
5 getstatic java.lang.System.out : java.io.PrintStream [16]
8 aload_0 [b]
9 iload_1 [i]
10 baload
11 invokevirtual java.io.PrintStream.print(int) : void [22]
14 iinc 1 1 [i]
17 iload_1 [i]
18 aload_0 [b]
19 arraylength
20 if_icmplt 5
23 getstatic java.lang.System.out : java.io.PrintStream [16]
26 invokevirtual java.io.PrintStream.println() : void [27]
29 aload_0 [b]
30 iconst_0
31 iconst_5
32 bastore
33 return
Line numbers:
[pc: 0, line: 5]
[pc: 5, line: 6]
[pc: 14, line: 5]
[pc: 23, line: 8]
[pc: 29, line: 10]
[pc: 33, line: 11]
Local variable table:
[pc: 0, pc: 34] local: b index: 0 type: byte[]
[pc: 2, pc: 23] local: i index: 1 type: int
Stack map table: number of frames 2
[pc: 5, append: {int}]
[pc: 17, same]
// Method descriptor #36 ([Ljava/lang/String;)V
// Stack: 4, Locals: 3
public static void main(java.lang.String[] args);
0 iconst_3
1 newarray byte [8]
3 dup
4 iconst_0
5 iconst_1
6 bastore
7 dup
8 iconst_1
9 iconst_2
10 bastore
11 dup
12 iconst_2
13 iconst_3
14 bastore
15 astore_1 [a]
16 aload_1 [a]
17 invokestatic com.blogpost.hiro99ma.FinalTest.print(byte[]) : void [37]
20 iconst_0
21 istore_2 [i]
22 goto 37
25 getstatic java.lang.System.out : java.io.PrintStream [16]
28 aload_1 [a]
29 iload_2 [i]
30 baload
31 invokevirtual java.io.PrintStream.print(int) : void [22]
34 iinc 2 1 [i]
37 iload_2 [i]
38 aload_1 [a]
39 arraylength
40 if_icmplt 25
43 getstatic java.lang.System.out : java.io.PrintStream [16]
46 invokevirtual java.io.PrintStream.println() : void [27]
49 return
Line numbers:
[pc: 0, line: 14]
[pc: 16, line: 15]
[pc: 20, line: 17]
[pc: 25, line: 18]
[pc: 34, line: 17]
[pc: 43, line: 20]
[pc: 49, line: 21]
Local variable table:
[pc: 0, pc: 50] local: args index: 0 type: java.lang.String[]
[pc: 16, pc: 50] local: a index: 1 type: byte[]
[pc: 22, pc: 43] local: i index: 2 type: int
Stack map table: number of frames 2
[pc: 25, append: {byte[], int}]
[pc: 37, same]
}
まったく同じだ。
そもそも「static void print(int[] b);」なので、finalがなくなっている。
配列だからか?
プリミティブ型の配列だからなのか??
というところで、疲れ果てたので寝る。
Javaのvoid print(final byte[] b)って、CやC++だとvoid print(unsigned char* const b)になりますよね(ポインタの指す先は変更できるが、変数そのものへの代入が出来ないという意味で同じ)。こっちのconstも出力されるアセンブリはconstのない場合と大差ないものになると思います。
返信削除ああ、なるほど!
返信削除言われてみればそうですね。。
MMUみたいなのでやらない限り、コンパイラの役割ですね。
ありがとうございます。