Javaは新しいトレンドメカニズムとともに拡大しており、それとともに、Vector、Finalization、NashornScriptEngine、SecurityManager、Unsafeなどの時代遅れの機能で成長しています。 墓はどうやって埋まっているのか。 Javaは繁栄し、成長し、新しい機能、API、モジュールを蓄積しますが、同時に、時々古い皮膚を捨てます。一部の部品は余剰であることが証明され、他の部品はもはや現代のタスクに適していません。 彼らのいくつかを見ていきましょう。 墓の中には何があるのか。 Legacyコレクション Java が存在する以前の時代に で、 あるいは 野生 で、 そして、 すべての後の古代物と異なり、この時代遅れのAPIは、 しかし、同時に、The クラスごとに「DON'T USE ME!」と叫ぶと、インターネットの広大さでは、これらの時代遅れのクラスは一般に「DON'T USE ME! . List Deque Map Vector Stack Dictionary Deprecated 文書化 legacy collection classes しかし、なぜ時代遅れなのでしょうか。 Legacyコレクションには2つの主な欠点があります: all interaction points with these classes are synchronized; クラスには共通のインターフェースがなく、個々のアプローチが必要です。 順番に進みましょう。 最初の欠点は、これらのクラスとのすべての相互作用ポイントが同期されていることです。 結局のところ、セキュアなマルチトレードは常に良いことであるが、これらのコレクションがシングルトレードの文脈で使用される場合どうなるか? 同期は単にパフォーマンスを削減することなく、実際のセキュリティ上の利点を提供する。 2つ目の欠点は、クラスには共通のインターフェイスがなく、個々のアプローチが必要であることです。 これは、プログラミング自体と同じくらい古い問題につながります:非普遍的なコード. ただ、我々は古いJavaバージョンで働く開発者であることを想像して、我々はコードに縛られている。 例えば、こちらは1つ: Stack Stack<Integer> stack = new Stack<>(); for (int i = 0; i < 5; i++) { stack.add(i); } System.out.println(stack.pop()); 今のところ、コードは何のヒッチもなく、しかし、それは150の参照で1000行に浸透していることを想像してください。 変数 stack その後、ある日、顧客は私たちにファックスを送り、彼らは非常にゆっくりと働いたと言った(クライアントではなくコードブロック)。 要素を保存するマレーを含み、このマレーはあまりにも頻繁に再生されます。 Stack 私たちは考えて理論を提示します:リンクされたリストは、アプリのパフォーマンスを100倍向上させるのに役立ちます!それをテストするために熱心に、私たちはいくつか見つけます。 インターネット上で、その後、私たちの袖を巻き上げて、すべての150の参照をコピーし、再現します。 面白い!まったくないけど、他の出口は見えません。 LinkedList stack 今のところに移り、コードはこんな感じです。 Deque<Integer> deque = new ArrayDeque<>(); IntStream.range(0, 5) .forEach(deque::addLast); System.out.println(deque.pollLast()); 時間の経過とともに、コードは150の参照も含むようになったが、今では参照がある。 We follow the same path and also put forward the theory that it will be faster with a linked list. And now... we just change the declaration from 2位 また、実施するもの そして、それだけが、必要なこと! deque ArrayDeque LinkedList Deque まさにこのように、The 共通のインターフェイスが欠けている問題を解決しました. それは特定の実装に縛られるのを避けるのに役立ちます. たとえば、開発者はもはや何かのようなものに閉じ込められていません。 and can use the インタフェースの代わりに コレクションフレームワーク Vector List 完成 あるとき、 (※) )は優れたアイデアのように思えた:オブジェクトが破壊されるときに呼ばれる方法で、システムリソースを解放する。 ファイナル #finalize() But, in practice, this idea wasn't so brilliant: どこかでオブジェクトへの参照がまだあったり、ゴミ収集者がそれを削除したくない場合に、フィニミュレーターはまったく呼ばれないかもしれません。 ゴミコレクターがオブジェクトを破壊したいと思っていたら、このコードが実行されるまで待たなければならなかった。 物体自体が復活する可能性もある。 最悪なのは、フィニマリストがメモリ漏れのための完璧な条件を作り出したことだ。 現代のJavaは非常に柔軟なものであり、ゴミ収集器さえもあらゆる種類のゴミ収集器であり、さまざまな形で構成することができる。 ゴミコレクターは、少なくとも1つの強力な参照がある限りインスタンスを収集しないことを忘れないでください。 これらの理由から、システムリソースをリリースする論理は、オブジェクトのライフサイクルに依存してはならない。 ドキュメンタリーを調べてみると、 方法は、使用することをお勧めします。 インタフェースの代わりに、ゴミコレクターを関与せずにリソースを効率的にリリースするために特別に作成されました。 するために。 #finalize() AutoCloseable 忘れないこと オブジェクトが実際に削除されたときの追跡に関しては、Javaは代替を提供します。 上記の問題を解決する方法です。 java.lang.ref.Cleaner 明確性のために、Javaメモリで遊び、フィニマレーを使用していくつかの不安全なコードを書きましょう。 class ImmortalObject { private static Collection<ImmortalObject> triedToDelete = new ArrayList<>(); @Override protected void finalize() throws Throwable { triedToDelete.add(this); } } ここでは、我々は文字通りあらゆる例を復活させる。 ゴミコレクターが削除する予定で、RAMやあなた自身を喜ばせることはありません。 . ImmortalObject OutOfMemoryError This is what the equivalent code looks like. これが同等のコードの外観です。 : Cleaner static List<ImmortalObject> immortalObjects = new ArrayList<>(); .... var immortalObject = new ImmortalObject(); Cleaner.create() .register( immortalObject, () -> immortalObjects.add(immortalObject) ); また、既存のオブジェクトの削除を聞くこともできるという点も注目すべきです。 止めましょう! なぜそのアプローチが問題であるかを知ったばかりですか? それは事実ですが、リソース管理が悪いだけです. 時にはJavaアプリケーションがゴミ収集器がオブジェクトを削除することを知る必要があることもあります. 何のために? たとえば、最適化されたキャッシュを作成するために。 クラスはまだ名前まで生きていますが、インスタンスを削除したときに静的リストに追加するためではありません。 聞き手がここで言及しているように、 現在、ゴミコレクターがインスタンスを削除しないため、オブジェクトは復活されません。Javaではよりイディオマティックで、ネクロマンスは関与していません。 Runnable immortalObject Finalizer は、JNI で作成されたオブジェクトをJava コード以外の場所で使用する場合、たとえば C++ で使用されていたが、このメカニズムは不確実であり、最終的に Finalizer はよりエレガントなオプションに置き換えられた。 ナチュラルエンジン Javaはかつて文字通りJavaScriptとペースを維持していたことを覚えている人はほとんどいない:JDK 8には組み込まれたJavaが搭載されていた。 JS エンジンは、開発者が JS コードを直接 Java から実行できるようにします。 ナチュラ 私たちは、Javaを離れることなく、非常に人気のあるバナナパズルを実装することができます。 new NashornScriptEngineFactory() .getScriptEngine() .eval("('b' + 'a' + + 'a' + 'a').toLowerCase();"); 確かに、これは素晴らしいことですが、Javaの開発者がJavaScriptエンジンを維持しなければならないことに気づくまでです。 NashornはJavaScriptの急速な進化とともにスピードをとることができず、GraalVM Polyglotのようなポリグロットソリューションが現れました。これらの要因により、エンジンはわずか4つの言語バージョン(Java 11で)で時代遅れとなり、しばらくするとようやく完成しました。 Java 15 について 投げ捨て Overboard なぜ GraalVM Polyglot が優れているのか? Nashorn は Java に加えて JavaScript を実装しているが、Polyglot は Java と JavaScript を含む複数の言語をネイティブにサポートする JVM ベースのプラットフォーム全体を作成する。 安全管理者 Java 17 まで生き残ったもう一つの古いタイマー . SecurityManager ワイルド・アプレットの時代に、Javaコードがブラウザで直接実行されるようになったとき、 Java アプリケーションのセキュリティの基盤です。 SecurityManager applet は、ブラウザで直接実行され、アニメーション、グラフィックス、またはインタラクティブな要素を表示することができますが、厳しい制限の下で動作します。 applet は、ブラウザで直接実行され、アニメーション、グラフィックス、またはインタラクティブな要素を表示することができますが、厳しい制限の下で動作します。 時間は過ぎ去った野生 ようやくブラウザからデスクトップに移行し、ソリューションとして完全に消滅した。 企業向けのアプリケーションで頻繁に使われてきました。 applets SecurityManager ♪ But nothing lasts forever, and the Grim Reaper came for Java 17 は内部全体を削除し、何の論理もなくファサードのみを残しました。 , we'll only find the placeholder: 場所を発見します。 SecurityManager SecurityManager @Deprecated(since = "17", forRemoval = true) public static void setSecurityManager(SecurityManager sm) { throw new UnsupportedOperationException( "Setting a Security Manager is not supported"); } 全画面モード 全画面モード 全画面モード 全画面モード どうやって それは潜在的に危険な操作をキャプチャし、各スタック要素がそのようなアクションを実行する権利があるかどうかをチェックする内部JVMガードとして機能しました。 SecurityManager 正確に何をしたか リストは膨大なので、主なポイントだけを見ることをお勧めします。 SecurityManager ファイルシステムへのアクセス ネットワーク接続; プロセスを構築し、完成させ、 システムプロパティへのアクセス レッスンと反省の操作 これは、信頼されていないコードをサンドボックスに保管する必要があったブラウザアプリレットの時代にうまく機能しましたが、コンテナ化の発展に伴い、それは時代遅れになりました。 SecurityManager たとえ 最初はapplets 用に作成されていたが、他の文脈でも使用されていた。 私たちのPVS-Studioの静的分析器では、静的クラスイニシアライザーが分析されたコードで呼ばれるような面白い問題を解決するのに役立ちました。 分析のプロセスが終わる。 静的フィールドを分析する際に、そのような操作をブロックすることができ、後にASMに置き換えたもの。 SecurityManager 応援 System#exit SecurityManager ちなみに、アポレットとしてのアプローチは以前から中止されていましたが、 JDKから削除され始めたばかりです。 SecurityManager 世界は変わり、アプローチは変わり、そして、 行き去っている。 SecurityManager 不安全 Javaは不確実性を削除します。 この墓の最も伝説的な住民は、 . sun.misc.Unsafe このクラスは、通常の言語ルールがもはや適用されていないJVMの深さへの秘密のゲートウェイでした。 簡単にJavaアプリケーションをインストールできます。 . Unsafe SIGSEGV 例えば、こんな感じ: class Container { Object value; // (1) } // ✨ some magic to get unsafe unsafely ✨ Unsafe unsafe = ....; long Container_value = unsafe.objectFieldOffset(Container.class.getDeclaredField("value")); var container = new Container(); unsafe.getAndSetLong(container, Container_value, Long.MAX_VALUE); // (2) System.out.println(container.value); // (3) IN THE フィールド(1)、設定 (2)代わりに and try output the value to (3)(3) value Long.MAX_VALUE Object System.out 仮想マシンはこのトリックを好まないし、最後のラインで崩壊する。 オブジェクトへの実際の参照の代わりにゴミが含まれており、これはそのような容易なエラーの一例に過ぎません。 value しかし、なぜこのような危険なメカニズムがJavaに追加されたのか。 無政府主義者として生まれたのではなく、救世主として生まれた。 そして 標準図書館は、メモリ管理、原子操作、またはトレードブロックなどの低レベルの操作を効率的に実行しなければならなかった。 Unsafe VarHandle MemorySegment しかし、舌が帽子の下を見ることを禁じている場合は、上記のすべてをどうやって行うことができますか? 正しく、私たちはギャップを作ることができます。 として現れた Java自身のニーズを満たすツール Unsafe internal 開発者はJava内でのみ使用することを意図し、そのインスタンスへの外部アクセスを得ることから保護することもありましたが、Javaで起こったことはJavaに残っていません。 見よ、不安全な方法で「不安全」を捕らえる魔法。 Unsafe var Unsafe_theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); Unsafe_theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) Unsafe_theUnsafe.get(null); このオブジェクトは、私たちが望む限り仮想マシンを拷問することを可能にします。 しかし、Javaが彼らから隠そうとしていたものを手に入れる勇気を持った開発者を批判するのに焦らないでください。 たとえば、ネットワークで働くために人気のあるネッティ・ライブラリは、より効率的にならなければならなかった。 代替品が出たとき、ニッティ。 その2 Unsafe Unsafe 交換 では、どのような置き換えについて話しているのでしょうか。 1つではなく2つあります: Foreign Function & Memory API は、メモリ操作を置き換えます。 VarHandle APIは原子の操作を置き換える。 これらのAPIは、単に特定の方法を置き換えることに限定されていません。 たとえば、Foreign Function & Memory API では、面倒な JNI を使用せずにネイティブな機能を呼び出すことができます! Unsafe Javaの不安全性はゆっくりと忘れ去られており、条件付きの不安を恐れずに同じことをする新しい、よく設計された、そして強力なAPIを残しています。 . SIGSEGV なぜ墓は満たされているのか。 Javaは成長し、時代遅れのアイデアを捨て、より安全でクリーンでシンプルになり、新しいAPIは単に「別のモジュールを追加する」のではなく、より洗練された方法で古いギャップを閉じています。 同時に、仮想「墓」は重要な文化的役割を果たしています:それはアイデアや決定の進化を記録し、私たちが歩んだ道を思い出させ、有意義なプラットフォームの開発に必要な文脈を形成します。