2016-06-30 6 views
0

TheUnsafeについての記事を読んでいますが、C/C++とは違って、私たちは材料のオフセットを解決しなければならないということと、32bit VMと64bits VMが混在しています。 (または、私はデータへのすべてのオフセットが実際にそれらに影響するポインタ算術に基づいていると仮定しています)。TheUnsafeを使ってmemcpyを理解する

残念ながら、TheUnsafeの使用方法について書かれたものは、1つの記事(最初に起こったもの)だけでなく、他のすべてがそれからある程度まで貼り付けられています。作者が明らかに英語を話さなかったため、そのうち多くは存在しません。

私の質問は:

私はそのフィールド(またはフィールドのフィールド、またはフィールドを所有しているインスタンスに、フィールドの、フィールドのフィールド+ポインタのオフセットを見つけることができますどのように.. 。)データのサイズが数GBを有していてもよく考えるとどのように私は+オフセットメモリアドレス別のポインタにのmemcpyを実行するためにそれを使用することができます

をTheUnsafe

を使用して、ヒープを考慮すると、データ・アラインメントの上に直接制御を提供していますし、次の理由で断片化されている可能性があります。

1)私はVMがオフセット+ 10とfield2をオフセットsizeof(field1)+ 32に割り当てることを止めることは何もないと思いますか?

2)また、GCが大量のデータを移動し、サイズが1GBのフィールドが断片化することがあると想定します。

できるだけ説明したように、memcpyの動作も同じですか?

データがGCのために断片化されている場合は、もちろん、ヒープには次のデータの塊がある場所へのポインタがありますが、上記の単純なプロセスを使用してもそれをカバーしていません。

このためにデータをオフヒープにする必要がありますか?もしそうなら、TheUnsafeを使ってオフ・ヒープ・データを割り当てる方法、そのようなデータをインスタンスのフィールドとして機能させる方法、

質問をよく理解していない人には、知っておく必要がある詳細を尋ねることをお勧めします。

また、「アレイにコピーしてSystem.arraycopyを使用する必要があるすべてのオブジェクトを置いてください」と言われると、人々は答えを控えてください。この素晴らしいフォーラムでは、質問されたことに答えるのではなく、原則として、元の質問とは何の関係もない完全な代替ソリューションを提供します。

よろしくお願いします。

+0

なぜ、言語が使用されるように設計されているのではなく、これをしたいのですか? –

+0

主に好奇心から(できることと出来ないことの限界を知っている)、大きなサウンドファイルで作業し、同時にリアルタイムで画像を処理する必要のあるピースソフトウェアを書いているのでおそらくあまりにも複雑な私のアルゴリズムを使用しています。今のところ、私の実証は60fpsより遅く実行されます。これはパフォーマンスを向上させるかもしれないと思います。私は学問的な知識がないので、アルゴリズムを改善するための最善の方法は、非常に基本的なものです(できるだけ多くのインスタンス化、インラインのものを避け、可能であればビットシフトを使用するなど) – FinnTheHuman

+0

javadocsを読んだり、コードを使用して、例えばgithubやjdk自体に? – the8472

答えて

1

まず大きな警告:

フィールド(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がクラッシュしたり(良い場合)、その場所に気づかずにメモリをゴミ箱に入れてしまう可能性があります場合)。

+0

だから、大量のデータがすべてUnsafeを使用してマネージドヒープの外側に割り当てられ、その後削除された場合、GCに問題はありません。しかし、それはどうやって行なわれるだろうか? theUnsafeはちょうど私が割り当てたデータのポインタを私に与えますか? memcpyに似た、私はそれがオフヒープアドレスを持っていると考えて使用できる関数がありますか? – FinnTheHuman

+0

あなた自身のメモリ管理を実装するために、 'allocateMemory'(割り当てられたメモリブロックのアドレスを返す)、データをコピーするための' copyMemory'、メモリを再び解放する 'freeMemory'という関数があります。たとえば、http://www.docjar.com/docs/api/sun/misc/Unsafe.html @FinnTheHumanを参照してください。 –

関連する問題