なぜsun.misc.Unsafeが存在し、それを現実の世界でどのように使用できるのですか?[閉まっている]


267

先日、sun.misc.Unsafeパッケージに遭遇し、それができることには驚いていました。

もちろん、クラスは文書化されていませんが、それを使用する正当な理由があるかどうか疑問に思っていました。それを使用する必要がある場合、どのようなシナリオが発生する可能性がありますか?実際のシナリオではどのように使用するのでしょうか?

さらに、あなたがいる場合、それを必要とし、その何かがおそらくあなたのデザインに問題があることを示すものではありませんか?

Javaにこのクラスが含まれているのはなぜですか?


7
JDK開発者は現在、Java 9でこのAPIをパブリックAPIに変換できるかどうか検討しています。使用する場合は、アンケートに記入するのに5分かかります。surveymonkey.com / s / sun-misc-Unsafe
アンディリンチ

回答:


159

  1. VM「intrinsification」。つまり、CAS(Compare-And-Swap)はロックフリーハッシュテーブルで使用されます。例:sun.misc.Unsafe.compareAndSwapInt CASの特別な指示を含むネイティブコードに実際のJNI呼び出しを行うことができます。

    CASの詳細については、http://en.wikipedia.org/wiki/Compare-and-swapをご覧ください。

  2. ホストVMのsun.misc.Unsafe機能を使用して、初期化されていないオブジェクトを割り当て、コンストラクターの呼び出しを他のメソッド呼び出しとして解釈できます。

  3. ネイティブアドレスからデータを追跡できます.java.lang.Unsafeクラスを使用してオブジェクトのメモリアドレスを取得し、安全でないget / putメソッドを介してそのフィールドを直接操作できます!

  4. JVMのコンパイル時間の最適化。「マジック」を使用した高パフォーマンスVM。低レベルの操作が必要です。例:http : //en.wikipedia.org/wiki/Jikes_RVM

  5. メモリの割り当て、sun.misc.Unsafe.allocateMemory例:ByteBuffer.allocateDirectが呼び出されたときに、DirectByteBufferコンストラクターが内部的にメモリを呼び出す

  6. 呼び出しスタックをトレースし、sun.misc.Unsafeによってインスタンス化された値で再生します。計測に役立ちます。

  7. sun.misc.Unsafe.arrayBaseOffsetおよびarrayIndexScaleを使用して、アレイレットを開発できます。これは、大きな配列を小さなオブジェクトに効率的に分割して、大きなオブジェクトのスキャン、更新、または移動操作のリアルタイムコストを制限する手法です。

  8. http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java

参照の詳細はこちら-http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html


1
Unsafeを使用してフィールドのアドレスを取得した場合、それは常にGCによって変更できるため、その操作はかなり役に立たないのではないですか?
pdeva

割り当てたアドレスを取得する
zudokod '15

が割り当てたものはどういう意味ですか?これは、「new」演算子を使用してオブジェクトが作成された場所で使用されているようです。したがって、私の質問です。
pdeva

1
unsafe.allocateMemoryと値を配置
zudokod

1
ポイント2について、他のメソッド呼び出しとしてコンストラクターを呼び出す方法を教えてください。バイトコードでない限り、それを行う方法が見つからなかったからです。
ミゲルガンボア

31

いくつかのコード検索エンジンで検索を実行しただけで、次の例が得られます。

{@link Unsafe}オブジェクトへのアクセスを取得する単純なクラス。{@link Unsafe} *は、アレイでの効率的なCAS操作を可能にするために必要です。{@link java.util.concurrent.atomic.AtomicLongArray}などの{@link java.util.concurrent.atomic}のバージョンでは、これらのアルゴリズムでは一般的に必要とされず、高価でもある追加のメモリ順序の保証が必要であることに注意してください。ほとんどのプロセッサで。

/ **静的フィールドのsun.misc.UnsafeベースのFieldAccessorsの基本クラス。リフレクションコードの観点から見ると、フィールドは9種類しかないことがわかります。8つのプリミティブ型とオブジェクトです。生成されたバイトコードの代わりにUnsafeクラスを使用すると、動的に生成されたFieldAccessorsのメモリと読み込み時間を節約できます。* /

  • SpikeSource

/ *回線を介して送信されるFinalFields ..受信側でオブジェクトを非整列化して再作成する方法 最終フィールドの値を確立するため、コンストラクターを呼び出したくありません。最終フィールドを送信側とまったく同じように再作成する必要があります。sun.misc.Unsafeがこれを行います。* /

他にも多くの例があります。上記のリンクをたどってください...


25

興味深いことに、私はこのクラスについては聞いたこともありませんでした(おそらく本当に良いことです)。

心に浮かぶことの1つは、Unsafe#setMemoryを使用して、ある時点で機密情報(パスワード、キーなど)を含むバッファをゼロ化することです。「不変」オブジェクトのフィールドに対してこれを行うこともできます(ここでも、単純な古いリフレクションがここでもうまくいくと思います)。私はセキュリティの専門家ではありませんので、これを一粒の塩で味わってください。


4
I'd never even heard of this class...私はそれについて何度もあなたに言いました!ため息 + :(
Tim Bender

7
Javaはコピー世代のガベージコレクターを使用しており、機密情報はおそらく上書きされるのを待っている「空き」メモリのどこかにすでに配置されているため、何の意味もありません。
ダニエルキャシディ

39
聞いたことはありませんが、私は彼らのpark()ドキュメントが大好きです:「現在のスレッドをブロックし、バランシングのアンパークが発生したとき、またはバランシングのアンパークがすでに発生したとき、またはスレッドが中断されたとき、または絶対的ではなく時間がゼロでない場合所定の時間ナノ秒が経過したか、絶対の場合は、エポックが経過してからのミリ秒単位の所定の期限、または誤って(つまり、「理由」がないために戻った)。「プログラムの終了時、またはランダムな間隔で、どちらか早い方にメモリが解放される」とほぼ同じです。
2011

1
@ダニエル、興味深い、私はそれを考慮していませんでした。これで、私がセキュリティの専門家ではない理由がわかります。:)
マイクダニエルズ

22

参照トレースにEclipseを使用したJava 1.6.12ライブラリの非常に簡単な分析に基づくと、のすべての便利な機能がUnsafe便利な方法で公開されているようです。

CAS操作は、Atomic *クラスを通じて公開されます。メモリ操作関数は、DirectByteBuffer同期を通じて公開されます。同期命令(park、unpark)は、AbstractQueuedSynchronizerを通じて公開されます。これは、Lock実装によって使用されます。


AtomicXXXUpdatersは遅すぎて、本当に必要なときに:CAS-実際に使用する余裕はありません。金属を使用する場合は、抽象化レベルと多数のチェックを使用しません。CASを失敗させることはループespで悪いです。ハードウェアが分岐の予測を誤ったと判断した場合(競合が多いため)に、比較/分岐の数が少ないと痛いだけです。パーク/アンパークは、LockSupportAQSではなく公開されます(後者はパーク/アンパークよりもロックの実装です)
bestsss

21

Unsafe.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();
    }
}

14
あなたは同じワット/行うことができThread.stop(Throwable)ますが、とにかく何かを投げることができる同じスレッドで安全ではないの必要はありませんが、(何のコンパイルのチェックはありません)
bestsss

これは純粋にバイトコードを介して行うことができます(またはLombocを使用してそれを行うことができます)
Antimony

1
@bestsssそのメソッドはスタブさUnsupportedOperationExceptionれ、Java 8の現在のスレッドでをスローします。ただし、スローする引数なしのバージョンはThreadDeath引き続き機能します。
gparyani 2014年

@damryfbfnetsi、私はかなり長い間コアjdkの議論をフォローしておらず、Java 8に移行する予定もありません。メソッドはスロー可能オブジェクトを宣言していますが、スローされた例外に関するメタデータは自由に破棄できるため、下位互換性がない可能性があります。
bestsss 2014年

10

安全でないクラス

低レベルで安全でない操作を実行するためのメソッドのコレクション。クラスとすべてのメソッドはパブリックですが、信頼できるコードのみがインスタンスを取得できるため、このクラスの使用は制限されています。

それの1つの使用法はjava.util.concurrent.atomicクラスです。


6

効率的なメモリコピー(少なくとも短いブロックのSystem.arraycopy()よりも高速にコピーするため); Java LZFおよびSnappyコーデックで使用されます。それらは「getLong」と「putLong」を使用します。これらはバイト単位のコピーよりも高速です。16/32/64バイトブロックのようなものをコピーするときに特に効率的です。


1
doh、arraycopyはx86-64でSSEループを使用しますgetLong/putLong(これはアドレスも計算する必要があります)
bestsss

これを実際に測定しましたか?組み合わせを使用しているときに短いブロックのために私はx86-64での一貫優れたパフォーマンスを参照してくださいgetLong/ putLong:理想的に私が好むSystem.arraycopy()シンプルさと、すべてのために。しかし、実際のテストでは、私がテストしたケースについてそれ以外のことが示されました。
StaxMan 2014年

はい、安全でないものを使用しているので、空気を抜くことによる意味のあるパフォーマンスはありませんでした。コンパイラーが長さをチェックする必要がある場合、大規模な配列で数バイト長のコピーに対してget / putLongが実際に機能する可能性があります。いくつかの実装。System.arrayCopyの後にメモリフェンスを追加します(ただし、無効化/有効化できます)。これが実際の原因である可能性があります。
bestsss

OK。新しいJDKがこれを変更した可能性があります。(JDK 1.6で)より高速な動作を観察したとき、私も驚きました。あるいは、使用方法の特定の違いを忘れているのかもしれません。これらは機能する場合でも、トリッキーな(場合によっては不安定な)最適化であり、効果を測定することが不可欠です。
StaxMan 2014年

5

私は最近、JVMの再実装に取り​​組んでおり、の観点から驚くほど多くのクラスが実装されていることがわかりましたUnsafe。クラスは主にJavaライブラリの実装者向けに設計されており、基本的に安全ではないが高速プリミティブの構築に必要な機能が含まれています。たとえば、生のフィールドオフセットの取得と書き込み、ハードウェアレベルの同期の使用、メモリの割り当てと解放などの方法があります。これは、通常のJavaプログラマによる使用を目的としたものではありません。文書化されておらず、実装固有であり、本質的に安全ではありません(そのため、名前です!)。また、SecurityManagerほとんどの場合、へのアクセスは許可されないと思います。

要するに、ライブラリの実装者がAtomicIntegerネイティブのような特定のクラスのすべてのメソッドを宣言する必要なしに、基礎となるマシンにアクセスできるようにするために主に存在します。ルーチンのJavaプログラミングでそれを使用したり心配したりする必要はありません。全体の要点は、そのようなアクセスを必要としないほど高速に残りのライブラリを作成することです。


実際、SecurityManagerは、リフレクションが無効になっている場合にのみアクセスを許可しません
amara

@ sparkleshy-これについて詳しく説明できますか?
templatetypedef

getUnsafeからインスタンスを取得することは、かなり厳格な要件を持っていながらUnsafe.class.getDeclaredField("theUnsafe")して.setAccessible(true)、その後.get(null)もそれを取得します
アマラ

@sparkleshy-機能していることに驚いています-セキュリティマネージャーがフラグを立てているはずです。
templatetypedef

5

独自のボクセルエンジンなどで、大量のメモリに効率的にアクセスして割り当てるために使用してください!(Minecraftスタイルのゲームなど)。

私の経験では、JVMは、本当に必要な場所で境界チェックを排除できないことがよくあります。たとえば、大きな配列を反復処理しているが、実際のメモリアクセスがループ内の非virtual *メソッド呼び出しの下に隠れている場合、JVMは直前に1回ではなく、各配列アクセスで境界チェックを実行する可能性がありますループ。したがって、パフォーマンスが大幅に向上する可能性がある場合は、sun.misc.Unsafeを使用してメモリに直接アクセスするメソッドを使用してループ内のJVM境界チェックを排除し、正しい場所で境界チェックを自分で実行できるようにします。(あなたあるレベルで境界チェックをするつもりですよね?)
*非仮想とは、特定のメソッドが何であれ、JVMが動的に解決する必要がないことを意味します。これは、クラス/メソッド/インスタンスが静的/最終/何を持っているかの組み合わせであることを正しく保証しているためです。

私の自家製ボクセルエンジンの場合、これにより、チャンクの生成とシリアル化(配列全体を一度に読み取り/書き込みを行っていた場所)中に劇的なパフォーマンスが向上しました。結果は異なる場合がありますが、境界除去の欠如が問題である場合は、これで修正されます。

これには潜在的に大きな問題がいくつかあります。具体的には、境界のチェックなしでメモリにアクセスする機能をインターフェースのクライアントに提供すると、おそらくそれを乱用します。(ハッカーがインターフェイスのクライアントにもなることを忘れないでください...特にJavaで記述されたボクセルエンジンの場合)。したがって、メモリアクセスが悪用されないようにインターフェイスを設計するか、危険なインターフェース混ざる前に、ユーザーデータを検証するように細心の注意を払う必要があります。ハッカーがチェックされていないメモリアクセスを使用してできる破滅的なことを考えると、おそらく両方のアプローチを取るのが最善です。


4

オフヒープコレクションは、大量のメモリを割り当て、GCの干渉なしに使用直後に割り当てを解除するのに役立ちます。に基づいて、ヒープ以外の配列/リストを操作するためのライブラリを作成しましたsun.misc.Unsafe


4

Unsafeを使用して、配列、ハッシュマップ、ツリーマップなどの膨大なコレクションを実装しました。
また、断片化を回避/最小化するために、安全ではないdlmallocの概念を使用してメモリアロケーターを実装しました。
これにより、同時実行でパフォーマンスを向上させることができました。


3

Unsafe.park()そしてUnsafe.unpark()カスタム同時実行制御構造と協力スケジューリングメカニズムの構築のために。


24
公的に入手可能java.util.concurrent.locks.LockSupport
bestsss

1

私自身はそれを使用していませんが、複数のスレッドでたまにしか読み取られない変数がある場合は(それを実際に揮発性にしたくないので)putObjectVolatile、メインスレッドで書き込むときにを使用して、readObjectVolatile他のスレッドからのまれな読み取りを行うとき。


1
しかし、以下のスレッドでの議論によると、uncontented揮発性、非揮発性物質は、とにかくと同じくらい速いですstackoverflow.com/questions/5573782/...
pdeva

揮発性のセマンティクスをプレーンな書き込みと揮発性の読み取りに置き換えることはできません...ある設定では機能するが別の設定では機能しない可能性があるため、これは災害のレシピです。単一のライタースレッドで揮発性のセマンティクスが必要な場合は、書き込みスレッドでAtomicReference.lazySetを使用し、リーダーでget()を使用できます(このトピックに関する説明については、この投稿を参照してください)。揮発性読み取りは比較的安価ですが、無料ではありません。こちらを参照してください
日産ワカルト2013年

「...書き込み時にputObjectVolatileを使用できます...」単純な書き込みを提案していませんでした。
Matt Crinklaw-Vogt 2013

1

現在使用しているクラスの1つが提供する機能を置き換える必要がある場合に必要です。

これは、カスタム/高速/よりコンパクトなシリアライゼーション/デシリアライゼーション、より高速/より大きなバッファー/サイ​​ズ変更可能なバージョンのByteBuffer、またはアトミック変数(現在サポートされていないもの)の追加などです。

私はいつかこれらすべてにそれを使用しました。



0

オブジェクトは、Javaコードが通常許可するレベルよりも低いレベルで動作する可用性であるように見えます。高レベルのアプリケーションをコーディングしている場合、JVMはメモリ処理やその他の操作をコードレベルから分離しているため、プログラミングが簡単です。Unsafeライブラリを使用することで、通常行われる低レベルの操作を効果的に完了できます。

woliveirajrが述べたように、「random()」は、他の多くの操作がUnsafeに含まれるallocateMemory()関数を使用するのと同じように、Unsafeを使用してシードします。

プログラマーとして、おそらくこのライブラリーを必要とせずに済むかもしれませんが、低レベルの要素を厳密に制御できると便利です(そのため、主要な製品でアセンブリと(それほどではありませんが)Cコードが流布しています)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.