LinkedList の方が高速でしょうか? `for each` を `iterator` に置き換えるべきでしょうか? この `ArrayList` は `Array` にすべきでしょうか? この記事は、非常に悪意のある最適化への対応として書かれたもので、私の記憶に永久に刻み込まれています。 Java と、ガベージ コレクターまたはコンテキスト切り替えによる干渉に対処する方法について詳しく説明する前に、まず将来の自分のために、コード記述の基礎について簡単に説明します。 時期尚早な最適化は諸悪の根源です。 これまでにも聞いたことがあると思いますが、時期尚早な最適化は諸悪の根源です。まあ、時々はそうなります。ソフトウェアを作成するときは、次の点を強く信じています。 、物語を書いているかのように意図を語るようにしてください。 できるだけ詳細に あります。つまり、言語の基礎を理解し、それに応じて適用する必要があります。 可能な限り最適にする必要が できるだけ詳しく説明 コードは意図を表す必要があり、その多くはメソッドや変数に名前を付ける方法に関係しています。 int[10] array1; // bad int[10] numItems; // better int[10] backPackItems; // great 変数名だけで、機能性を推測できます。 は抽象的ですが、 予想される動作について多くのことを伝えます。 numItems backPackItems あるいは、次のようなメソッドがあるとします。 List<Countries> visitedCountries() { if(noCountryVisitedYet) return new ArrayList<>(0); } // (...) return listOfVisitedCountries; } コードに関する限り、これはほぼ問題ないように見えます。 もっと良いものが作れるでしょうか? 絶対できます! List<Countries> visitedCountries() { if(noCountryVisitedYet) return Collections.emptyList(); } // (...) return listOfVisitedCountries; } を読むほう を読むよりもはるかに説明的です。 Collections.emptyList() new ArrayList<>(0); 上記のコードを初めて読んでいて、ユーザーが実際に国を訪問したことがあるかどうかを確認する につまずいたと想像してください。また、これが長いクラスに埋め込まれていると想像してください と読む方が、 と読むよりも明らかに説明的です。また、クライアント コードが変更できないように不変であることも確認します。 ガード句 Collections.emptyList() new ArrayList<>(0) 可能な限り最適に 自分の言語を理解し、それに応じて使用してください。 必要な場合は、 オブジェクトでラップする必要はありません。 実際に必要なのは だけである場合は、 を使用する場合も同様です。 double Double Array List スレッド間で状態を共有する場合は、 または を使用して文字列を連結する必要があることに注意してください。 StringBuilder StringBuffer // don't do this String votesByCounty = ""; for (County county : counties) { votesByCounty += county.toString(); } // do this instead StringBuilder votesByCounty = new StringBuilder(); for (County county : counties) { votesByCounty.append(county.toString()); } データベースのインデックス作成方法を理解してください。ボトルネックを予測し、それに応じてキャッシュします。上記はすべて最適化です。これらは、第一人者として認識し、実装する必要がある種類の最適化です。 最初にどうやって殺すのか? 数年前に読んだハッキング記事のことは、決して忘れません。実を言うと、著者はすぐに撤回しましたが、それは、善意からいかに多くの悪が生まれるかを示しています。 // do not do this, ever! int i = 0; while (i<10000000) { // business logic if (i % 3000 == 0) { //prevent long gc try { Thread.sleep(0); } catch (Ignored e) { } } } 地獄のガベージコレクターハック! 上記のコードがなぜ、どのように機能するかについては、 詳しく読むことができます。このエクスプロイトは確かに興味深いものですが、これは決してしてはいけ ことの 1 つです。 元の記事で ない 副作用によって動作します。このブロックでは は意味がありません。 Thread.sleep(0) 下流のコードの欠陥を悪用して動作する このコードを受け継ぐ人にとっては、それは難解で魔法のようだ すべて使って記述した後でボトルネックに遭遇した場合にのみ、もう少し複雑なものを作成し始めてください。ただし、上記のような作り込みは避けてください。 言語が提供するデフォルトの最適化を ガベージコレクター 対処するにはどうすればいいですか? に すべて完了した後でも、ガベージ コレクターがまだ抵抗を示している場合は、次のことを試してください。 サービスがレイテンシに非常に敏感で GC を許可できない場合は、 。 「Epsilon GC」を使用して実行し、GC を完全に回避します -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC これは明らかにOOM例外が発生するまでメモリを増大させるので、これは一時的なシナリオか、プログラムがオブジェクトを作成しないように最適化されているかのどちらかです。 サービスがレイテンシに敏感だが、 は、GC1 を実行し、 (デフォルトは 250 ミリ秒) のような値を入力します。 許容範囲に余裕がある場合 -XX:MaxGCPauseTimeMillis=100 、たとえば、そのうちの1つがストップザワールドガベージコレクターである または を呼び出す場合 を指定して実行することで、問題の動作をオーバーライドできます。 問題が外部ライブラリから発生する場合 System.gc() Runtime.getRuntime().gc() -XX:+DisableExplicitGC JVM 11 以上で実行している場合は、 試してみてください。パフォーマンスが大幅に向上します。 。この も確認することをお勧めします。 Z ガベージ コレクター (ZGC) を -XX:+UnlockExperimentalVMOptions -XX:+UseZGC JDK 21 GC ベンチマーク バージョン開始 バージョン終了 デフォルトGC ジャワ1 Java 4 シリアルガベージコレクター Java 5 Java 8 並列ガベージコレクター Java9 について 進行中 G1 ガベージコレクター 注 1: Java 15 以降、 が、 を使用して明示的にアクティブ化する必要があります。 ZGC 本番環境で使用可能です -XX:+UseZGC 注 2: VM は、2 つ以上のプロセッサと 1792 MB 以上のヒープ サイズを検出すると、マシンをサーバー クラスと見なします。サーバー クラスでない場合は、 。 デフォルトでシリアル GC になります 基本的に、アプリケーションのパフォーマンス制約がガベージ コレクションの動作に直接関連していることが明らかで、十分な情報に基づいた調整を行うために必要な専門知識がある場合に、GC チューニングを選択します。それ以外の場合は、JVM のデフォルト設定を信頼し、アプリケーション レベルのコードの最適化に重点を置きます。 - を読んでみてください u/shiphe コメント全文 他にも調べてみたい関連ライブラリ: Java マイクロベンチマーク ハーネス (JMH) 実際のベンチマークを行わずに感覚で 行うと、自分自身に不利益をもたらします。JMH は、アルゴリズムのパフォーマンスをテストするための Java ライブラリです。これを使用してください。 最適化を 事実上の Java スレッド アフィニティ プロセスを特定のコアに固定すると、キャッシュ ヒットが改善される可能性があります。これは、基盤となるハードウェアと、ルーチンがデータを処理する方法によって異なります。とはいえ、このライブラリを使用すると実装が非常に簡単なので、CPU を集中的に使用するメソッドが問題になっている場合は、テストしてみることをお勧めします。 LMAXディスラプター これは、たとえ必要でなくても、勉強したいと思うライブラリの 1 つです。その目的は、超低レイテンシの同時実行を可能にすることです。しかし、 から 実装方法には多くの新しい概念がもたらされます。7 年前に初めてこのライブラリを発見したとき、それを理解するために徹夜したことを今でも覚えています。 機械的な共感 リング バッファーまで、 Netflix jvmquake の前提は、JVM に問題が発生した場合、JVM がハングアップするのではなく、停止することを望むというものです。数年前、私はメモリ制約が厳しい HTCondor クラスターでシミュレーションを実行していましたが、時々、「メモリ不足」エラーが原因でジョブが停止することがありました。 jvmquake このライブラリは JVM を強制終了させ、実際のエラーに対処できるようにします。この特定のケースでは、HTCondor はジョブを自動的に再スケジュールします。 最終的な考え 私がこの記事を書くきっかけとなったコードとは?私はもっとひどいコードを書いたことがあります。今でも書いています。私たちが期待できる最善のことは、継続的に間違いを減らすことです。 数年後に自分のコードを見て不満を感じることになるだろうと予想しています。 それは良い兆候です。 この記事とその他の記事はwasteofserver.comでご覧いただけます。 編集と感謝: の可変性エラーをキャッチし、詳細な説明をしてくれた に感謝します。 visitedCountries() FlorianSchaetz シリアルGCが使えることを説明してくれた と に感謝します。 低スペックマシンでは u/brunocborges u/BikingSquirrel GC をいじるべき時といじる 時をわかりやすく説明してくれた へ べきでない u/shiphe さん ものに関して正しい方向に導いてくれた へ 標準的な慣行とみなされるべき u/tomwhoiscontrary JDK 21 ガベージ コレクター ベンチマークへのリンクを提供してくれた さん (再び) u/BikingSquirrel にも掲載されています wasteofserver.com