まず大きな警告:は
フィールド(I1)のオフセットを取得するには、「安全でないが死ななければならない」http://blog.takipi.com/still-unsafe-the-major-bug-in-java-6-that-turned-into-a-java-9-feature/
いくつかの前提条件
static class DataHolder {
int i1;
int i2;
int i3;
DataHolder d1;
DataHolder d2;
public DataHolder(int i1, int i2, int i3, DataHolder dh) {
this.i1 = i1;
this.i2 = i2;
this.i3 = i3;
this.d1 = dh;
this.d2 = this;
}
}
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
DataHolder dh1 = new DataHolder(11, 13, 17, null);
DataHolder dh2 = new DataHolder(23, 29, 31, dh1);
基本は、使用することができます次のコード:
Field fi1 = DataHolder.class.getDeclaredField("i1");
long oi1 = unsafe.objectFieldOffset(fi1);
およびアクセスは、インスタンスのフィールド値は、DH1、あなたは書くことができます
System.out.println(unsafe.getInt(dh1, oi1)); // will print 11
あなたがオブジェクト参照(D1)にアクセスするようなコードを使用することができます
:
Field fd1 = DataHolder.class.getDeclaredField("d1");
long od1 = unsafe.objectFieldOffset(fd1);
を、あなたが参照を取得するためにそれを使用することができますオフを取得するには
System.out.println(dh1 == unsafe.getObject(dh2, od1)); // will print true
フィールドの順序と位置合わせ
:DH2からDH1へオブジェクトのすべての宣言されたフィールドのETS:私のテストで
for (Field f: DataHolder.class.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
System.out.println(f.getName()+" "+unsafe.objectFieldOffset(f));
}
}
適当と考えるようJVMは、フィールドを並べ替えているようです(すなわち、フィールドを追加し、次の実行時に完全に異なるオフセットを得ることができる)
Anは、ガベージコレクタがするので、それは、次のコードは、遅かれ早かれ、あなたのJVMをクラッシュしようとしていることを理解することが重要ですネイティブメモリに
をアドレスオブジェクトいつ、なぜそれが起こるのかをあなたがコントロールすることなく、ランダムな時間にあなたのオブジェクトを動かすことができます。
また、次のコードはJVMの種類(32ビット対64ビット)とJVMのいくつかの開始パラメータ(64ビットJVM上の圧縮されたoopsの使用)に依存することを理解することが重要です。
32ビットVMでは、オブジェクトへの参照はintと同じサイズです。だからunsafe.getObject(dh2, od1))
の代わりにint addr = unsafe.getInt(dh2, od1));
と電話をすればどうなるのですか?それはオブジェクトのネイティブアドレスですか?
レッツ・トライ:予想通り
System.out.println(unsafe.getInt(null, unsafe.getInt(dh2, od1)+oi1));
が11
を出力します。圧縮おっと(-XX:-UseCompressedOops)なしの64ビットVM上で
、あなたは圧縮おっと(-XX:+ UseCompressedOops)と64ビットVM上で
System.out.println(unsafe.getInt(null, unsafe.getLong(dh2, od1)+oi1));
を記述する必要があります、物事があります少し複雑です。
System.out.println(unsafe.getInt(null, 8L*(0xffffffffL&(dh2, od1)+oi1));
これらのアクセス
問題は、このコードと一緒にガベージコレクタであるとの問題は何ですか:この亜種は8Lでそれらを乗じて64ビットのアドレスになっている32ビットのオブジェクト参照を持っています。ガベージコレクタは、オブジェクトが好きなときに移動することができます。 JVMはオブジェクト参照(ローカル変数dh1とdh2、これらのオブジェクトのフィールドd1とd2)を知っているので、これらの参照をそれに応じて調整することができます。
オブジェクト参照をint/long変数に抽出すると、オブジェクト参照はオブジェクト参照と同じビットパターンを持つプリミティブ値に変換されますが、ガベージコレクタはこれらがオブジェクト参照であることを認識しませんランダムジェネレータによっても生成されているため)、オブジェクトを動かしながらこれらの値を調整しません。ガベージコレクションのサイクルが始まると、抽出されたアドレスはもはや有効ではなくなり、これらのアドレスでメモリにアクセスしようとするとすぐにJVMがクラッシュしたり(良い場合)、その場所に気づかずにメモリをゴミ箱に入れてしまう可能性があります場合)。
なぜ、言語が使用されるように設計されているのではなく、これをしたいのですか? –
主に好奇心から(できることと出来ないことの限界を知っている)、大きなサウンドファイルで作業し、同時にリアルタイムで画像を処理する必要のあるピースソフトウェアを書いているのでおそらくあまりにも複雑な私のアルゴリズムを使用しています。今のところ、私の実証は60fpsより遅く実行されます。これはパフォーマンスを向上させるかもしれないと思います。私は学問的な知識がないので、アルゴリズムを改善するための最善の方法は、非常に基本的なものです(できるだけ多くのインスタンス化、インラインのものを避け、可能であればビットシフトを使用するなど) – FinnTheHuman
javadocsを読んだり、コードを使用して、例えばgithubやjdk自体に? – the8472