プロジェクトの歴史
すべてが美しく、雲一つない状態で始まりました。 ある大手銀行のニーズに合わせて、特定の投資ポートフォリオのバリューアットリスク値を計算する計算機を実装する必要がありました。 ほとんどの金融アプリケーションと同様に、この方法論は「重い」計算を意味するものではありませんが、データフローが巨大になることがあります。
大量のデータを処理する問題は、通常、よく知られた2つのタイプのスケーリング(垂直および水平)によって解決されます。 垂直では、すべてが合理的に受け入れられました。 16 GBのRAM、Red Hat、およびJava 1.6を備えた16コアのマシンを自由に使用できました。 そのようなハードウェアで非常に好転することが可能でした。実際、私たちは数か月間成功しています。
お客様が私たちをノックし、ITインフラストラクチャが改訂され、16x16の代わりに4x1-2があると言われるまで、すべてが完璧でした。
当然、アプリケーションのランタイム要件は数回増加しましたが、 当時の私たちの感情を伝えることは十分に困難でしたが、スピーチは突然、さまざまなall意、暗示、比較に強く慣れました。
最初の試み
まず、 Value-at-Risk計算機とは何かを説明します。 これは、大量のデータを通過する、多くの「単純な」計算を伴うプログラムです。
最も有用な最適化キー:
- -serverは非常に便利なキーです。JVMはループを拡張し、多くの機能をインライン化します。
- 文字列の操作:-XX:+ UseCompressedStrings、-XX:+ UseStringCache、-XX:+ OptimizeStringConcat
当然、このような単純なトリックでは望ましい結果が得られず、アプリケーションをさらに「縮小」する可能性の方向を掘り続けました。 最初に行うことを決定したのは、不要なキャッシュを削除し、既存のキャッシュを最適化することでした(80%のパフォーマンスを20%向上させるもの)。 不要なものはすぐに削除され、残りのものに取り組むために、Javaのさまざまなタイプのリンクを調べることにしました。
そのため、次のリンクタイプを使用できます。
- ハード/強い
- 柔らかい
- 弱い
- ファントム
それらの間の依存関係は次のようになります。
これらのタイプのリンクを扱うための仕様が保証するものを見てみましょう。
ハード/ストロングリンクは、「新しい」キーワードを使用したときに作成される最も一般的なリンクです。 作成されたオブジェクトへのリンクの数がゼロに達すると、そのようなリンクは削除されます。 仮想マシンにさらに作業を行うための十分なメモリがない場合、ソフトリンク(ソフト)を削除できます。 弱いリンク(弱い)は、GCが決定すればいつでも収集できます。 ファントムリンク(ファントム)は特別なタイプのリンクであり、従来のファイナライズよりも柔軟なファイルオブジェクト化に必要です。
ハードリンクとファントムリンクは、必要な機能と柔軟性を提供しないため、すぐに考慮から除外されました。 ハードは適切なタイミングで削除されませんが、すべてがファイルシステムで正常に削除されました。
たとえば、弱いリンクがどのように組み立てられるかを考えてみましょう。
オブジェクトが常にアクセス可能であり、いつでも削除できるという保証はありません。 この特異性のため、ソフトリンク用の内部のほとんどの「重い」キャッシュを再構築することが決定されました。 「オブジェクトを可能な限りキャッシュに保持しますが、メモリが不足している場合は、稼働時間の要件が増加したため、再度計算できます。」というステートメントに動機付けられました。
結果は重要でしたが、アプリケーションは切望された4GBでは機能しませんでした。
詳細な研究
さまざまなプロファイリングツールを使用して、さらに調査を実施しました。
- 標準JVMツール:-XX:+ PrintGCDetails、-XX:+ PrintGC、-XX:PrintReferenceGCなど
- MXBean
- Visualvm
分析とは異なり、データ収集にはそれほど時間がかかりませんでした。 数日、素晴らしい数字と文字を観察した後、次の結論が出されました。多くのオブジェクトが作成されており、古い世代は非常に過負荷になっています。 この問題を解決するために、さまざまなガベージコレクターとそれらの操作方法に注目し始めました。
まず、生成されるオブジェクトの数を減らす必要がありました。 ほとんどのデータの構造は「XXX1:XXX2:XXX3など」であることに注意してください。 「XXX」タイプのすべてのパターンは、プールからのオブジェクトへの参照に置き換えられたため、作成されたオブジェクトの数が大幅に減少し(約5倍)、貴重なメモリがさらに解放されました。
第二に、ガベージコレクション戦略をより詳細に扱うことにしました。 ご存じのとおり、次のガベージコレクション戦略を利用できます。
- シリアル
- 平行
- 並列圧縮
- 同時マークスイープ
- G1コレクター
Javaの6番目のバージョンが使用されていたため、G1は利用できませんでした。 シリアルとパラレルはそれほど違いはなく、私たちのタスクではあまりうまく現れていません。 並列圧縮は、データの最適化を減らすことができるフェーズのために興味深いものでした。 並行マークスイープは、ワールドストップフェーズの時間を短縮でき、強力なフラグメンテーションも許可されなかったという事実により、興味深いものでした。
Parallel compactingとConcurrent Mark-Sweepコレクターを比較した後、2番目に停止することが決定されました。これは良い解決策であることが判明しました。
上記のすべてのテクニックの戦闘テストの後、アプリケーションは新しい要件と完全に互換性があり、本番環境で正常に起動しました! 誰もが安reliefのため息をついた!
学んだ教訓
- 文字列を操作するためのキーが役立ちました:-XX:+ UseCompressedStrings、-XX:+ UseStringCache、-XX:+ OptimizeStringConcatおよび文字列データの詳細
- 使用するオブジェクトの数を減らす
- JVMの微調整には多くの時間がかかりますが、結果は正当化されています
- できるだけ早く要件を把握しましょう! :)
問題のより詳細なバージョンとソリューションの段階は、サンクトペテルブルクで開催される予定のJPoint会議で聞くことができます。