先日、私はSun.misc.Unsafeパッケージに出会い、それができることに驚きました。
もちろん、クラスは文書化されていませんが、それを使用する正当な理由があるかどうか疑問に思っていました。それを使用する必要がある場合、どのようなシナリオが発生する可能性がありますか?現実のシナリオでどのように使用できますか?
さらに、do必要な場合、デザインに何か問題がある可能性があることを示していませんか?
なぜJavaにこのクラスが含まれているのですか?
例
VM「intrinsification」。すなわち、ロックフリーハッシュテーブルで使用されるCAS(Compare-And-Swap)例:Sun.misc.Unsafe.compareAndSwapIntは、CASの特別な指示を含むネイティブコードに実際のJNI呼び出しを行うことができます。
Host VMのSun.misc.Unsafe機能を使用して、初期化されていないオブジェクトを割り当て、コンストラクター呼び出しを他のメソッド呼び出しとして解釈できます。
ネイティブアドレスからデータを追跡できます。Java.lang.Unsafeクラスを使用してオブジェクトのメモリアドレスを取得し、安全でないget/putメソッドを介してそのフィールドを直接操作できます。
JVMのコンパイル時間の最適化。 「マジック」を使用した高パフォーマンスVM。低レベルの操作が必要です。例: http://en.wikipedia.org/wiki/Jikes_RVM
メモリの割り当て、Sun.misc.Unsafe.allocateMemory例:-ByteBuffer.allocateDirectが呼び出されたときにDirectByteBufferコンストラクターが内部的に呼び出します
呼び出しスタックをトレースし、Sun.misc.Unsafeによってインスタンス化された値で再生します。インスツルメンテーションに役立ちます
Sun.misc.Unsafe.arrayBaseOffsetとarrayIndexScaleを使用してarrayletを開発できます。arrayletは、大きなオブジェクトのスキャン、更新、移動のリアルタイムコストを制限するために、大きな配列を小さなオブジェクトに効率的に分割する手法です。
http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-Java
参照の詳細はこちら- http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html
いくつかのコード検索エンジンで search を実行するだけで、次の例を取得できます。
{@link Unsafe}オブジェクトへのアクセスを取得する単純なクラス。 {@link Unsafe} *は、アレイでの効率的なCAS操作を可能にするために必要です。 {@link Java.util.concurrent.atomic.AtomicLongArray}などの{@link Java.util.concurrent.atomic}のバージョンには、これらのアルゴリズムでは通常必要なく、また高価な追加のメモリ順序保証が必要であることに注意してください。ほとんどのプロセッサで。
/ **静的フィールドのSun.misc.UnsafeベースのFieldAccessorsの基本クラス。観察では、リフレクションコードの観点から9つのタイプのフィールド(8つのプリミティブタイプとオブジェクト)しかありません。生成されたバイトコードの代わりにUnsafeクラスを使用すると、動的に生成されたFieldAccessorsのメモリとロード時間が節約されます。 * /
/ *ワイヤを介して送信されるFinalFields ..受信側でオブジェクトを非整列化および再作成する方法最終フィールドの値を確立するため、コンストラクターを呼び出したくありません。最終フィールドは、送信者側とまったく同じように再作成する必要があります。 Sun.misc.Unsafeはこれを行います。 * /
他にも多くの例があります。上記のリンクをたどってください...
興味深いことに、私はこのクラスのことすら聞いたことがありませんでした(おそらく、本当に良いことです)。
思いがけないことの1つは、 nsafe#setMemory を使用して、ある時点で機密情報(パスワード、キーなど)を含むバッファーをゼロ化することです。 「不変」オブジェクトのフィールドに対してもこれを行うことができます(この場合も、単純な古いリフレクションがこのトリックを行う可能性があると思います)。私はセキュリティの専門家ではありませんので、これを一粒の塩で取ります。
参照トレースにEclipseを使用したJava 1.6.12ライブラリの非常に簡単な分析に基づくと、Unsafe
のすべての有用な機能が有用な方法で公開されているようです。
CAS操作はAtomic *クラスを通じて公開されます。メモリ操作関数は、DirectByteBuffer Sync命令(park、unpark)を通じて公開され、AbstractQueuedSynchronizerを通じて公開されます。AbstractQueuedSynchronizerはLock実装で使用されます。
nsafe.throwException-チェック例外を宣言せずにスローできます。
これは、リフレクションまたはAOPを扱う場合に役立ちます。
ユーザー定義のインターフェイス用の汎用プロキシを構築するとします。また、インターフェイスで例外を宣言するだけで、特別な場合に実装によってスローされる例外を指定できます。次に、これは、インターフェイスの動的実装でチェック例外を発生させる唯一の方法です。
import org.junit.Test;
/** need to allow forbidden references! */ import Sun.misc.Unsafe;
/**
* Demonstrate how to throw an undeclared checked exception.
* This is a hack, because it uses the forbidden Class {@link Sun.misc.Unsafe}.
*/
public class ExceptionTest {
/**
* A checked exception.
*/
public static class MyException extends Exception {
private static final long serialVersionUID = 5960664994726581924L;
}
/**
* Throw the Exception.
*/
@SuppressWarnings("restriction")
public static void throwUndeclared() {
getUnsafe().throwException(new MyException());
}
/**
* Return an instance of {@link Sun.misc.Unsafe}.
* @return THE instance
*/
@SuppressWarnings("restriction")
private static Unsafe getUnsafe() {
try {
Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
singleoneInstanceField.setAccessible(true);
return (Unsafe) singleoneInstanceField.get(null);
} catch (IllegalArgumentException e) {
throw createExceptionForObtainingUnsafe(e);
} catch (SecurityException e) {
throw createExceptionForObtainingUnsafe(e);
} catch (NoSuchFieldException e) {
throw createExceptionForObtainingUnsafe(e);
} catch (IllegalAccessException e) {
throw createExceptionForObtainingUnsafe(e);
}
}
private static RuntimeException createExceptionForObtainingUnsafe(final Throwable cause) {
return new RuntimeException("error while obtaining Sun.misc.Unsafe", cause);
}
/**
* scenario: test that an CheckedException {@link MyException} can be thrown
* from an method that not declare it.
*/
@Test(expected = MyException.class)
public void testUnsingUnsaveToThrowCheckedException() {
throwUndeclared();
}
}
クラス 安全でない
低レベルの安全でない操作を実行するためのメソッドのコレクション。クラスとすべてのメソッドはパブリックですが、信頼できるコードのみがインスタンスを取得できるため、このクラスの使用は制限されています。
Java.util.concurrent.atomic
クラスでの使用例:
私は最近、JVMの再実装に取り組んでいましたが、Unsafe
に関して驚くほど多くのクラスが実装されていることがわかりました。このクラスは主にJavaライブラリ実装者向けに設計されており、基本的に安全ではないが高速なプリミティブを構築するために必要な機能が含まれています。たとえば、ハードウェアレベルの同期の使用、メモリの割り当てと解放など、未加工のフィールドオフセットを取得および書き込むためのメソッドがあります。通常のJavaプログラマーによる使用は想定されていません。それは文書化されておらず、実装固有であり、本質的に安全ではありません(そのため名前です!)。さらに、SecurityManager
はほとんどすべての場合にアクセスを禁止すると思います。
要するに、主に、AtomicInteger
ネイティブのような特定のクラスのすべてのメソッドを宣言することなく、ライブラリ実装者が基礎となるマシンにアクセスできるようにするために存在します。ルーチンJavaプログラミングでそれを使用したり、心配したりする必要はありません。全体のポイントは、その種のアクセスを必要としないほど十分にライブラリの残りを速くすることです。
独自のボクセルエンジンなどで、大量のメモリに効率的にアクセスして割り当てるために使用します! (つまり、Minecraftスタイルのゲーム。)
私の経験では、JVMは本当に必要な場所で境界チェックを排除できないことがよくあります。たとえば、大きな配列を繰り返し処理しているが、実際のメモリアクセスがループ内の非仮想*メソッド呼び出しの下に隠れている場合、JVMは直前に1回ではなく、各配列アクセスで境界チェックを実行する場合がありますループ。したがって、潜在的に大きなパフォーマンスの向上のために、Sun.misc.Unsafeを使用してメモリに直接アクセスするメソッドを介してループ内のJVM境界チェックを削除し、正しい場所で境界チェックを行うようにしてください。 (あなたareあるレベルで境界チェックをするつもりですか?)
*非仮想では、クラス/メソッド/インスタンスがstatic/final/what-have-youの組み合わせであることを正しく保証しているため、JVMは特定のメソッドが何であれ動的に解決する必要はありません。
自作のボクセルエンジンの場合、これにより、チャンクの生成とシリアル化中に劇的なパフォーマンスの向上がもたらされました(アレイ全体を一度に読み書きする場所)。結果は異なる可能性がありますが、境界除去の欠如が問題である場合、これはそれを修正します。
これには潜在的に大きな問題がいくつかあります。具体的には、インターフェイスのクライアントに境界チェックを行わずにメモリにアクセスする機能を提供すると、おそらくそれを悪用します。 (特にJavaで記述されたVoxelエンジンの場合、ハッカーがインターフェイスのクライアントになることも忘れないでください。)したがって、メモリアクセスが悪用されないようにインターフェイスを設計するか、ユーザーデータを検証する前に、ユーザーデータが危険なインターフェイスと ever 混ざり合うように非常に注意する必要があります。ハッカーが未チェックのメモリアクセスで行うことができる壊滅的なことを考えると、おそらく両方のアプローチを取るのが最善です。
Unsafeを使用して、Arrays、HashMaps、TreeMapsなどの巨大なコレクションを実装しました。
そしてフラグメンテーションを回避/最小化するために、 dlmalloc の概念を使用して安全でないメモリアロケータを実装しました。
これにより、並行性のパフォーマンスを得ることができました。
オフヒープコレクションは、GCの干渉なしに、大量のメモリを割り当て、使用後すぐに割り当てを解除するのに役立つ場合があります。 Sun.misc.Unsafe
に基づいたオフヒープ配列/リストを操作するために library を作成しました。
Unsafe.park()
およびUnsafe.unpark()
カスタムの同時実行制御構造と協調スケジューリングメカニズムの構築用。
その使用例の1つに、ランダムメソッドがあります。これは、 シードを変更するために安全でないものを呼び出す です。
現在使用しているクラスの1つが提供する機能を置き換える必要がある場合に必要です。
これは、カスタム/高速/よりコンパクトなシリアル化/逆シリアル化、ByteBufferの高速/より大きいバッファ/サイズ変更可能なバージョン、またはアトミック変数の追加などです。現在サポートされていないもの。
いつかこれらすべてに使用しました。
自分で使用したことはありませんが、複数のスレッドによってまれにしか読み取れない変数がある場合は(揮発性にしたくない場合)、書き込み時にputObjectVolatile
を使用できます。メインスレッドおよびreadObjectVolatile
で、他のスレッドからのまれな読み取りを実行します。
オブジェクトは、Javaコードで通常許可されているレベルよりも低いレベルで動作する可用性があるように見えます。高レベルのアプリケーションをコーディングしている場合、JVMはメモリレベルの処理やその他の操作をコードレベルから抽象化するため、プログラミングが容易になります。 Unsafeライブラリを使用することにより、一般的に行われる低レベルの操作を効果的に完了できます。
Woliveirajrが述べたように、他の多くの操作がUnsafeに含まれるallocateMemory()関数を使用するように、「random()」はUnsafeを使用してシードします。
プログラマーとして、おそらくこのライブラリを必要とせずに逃げることができますが、低レベルの要素を厳密に制御できると便利です(そのため、主要製品にはまだアセンブリと(それほどではないが)Cコードが漂っています)