私は長い間、仮想マシンのソースコードを掘り下げたいと思っていましたが、それからGCを削除することは素晴らしいスタートです。 カットの下で、OpenJDKをアセンブルし、そこからガベージコレクターを切り取り、再度収集する方法を説明します。 最後に、あなたの頭に浮かんだ「なぜ」という質問に答えさえ与えられます。
ソース? さらに2つ与えて、バイナリを振りかけます!
メインコース
OpenJDKはフォレストを使用してMercurialに保存され、コードを取得する最も簡単な方法は
$ hg fclone http://hg.openjdk.java.net/jdk7/jdk7
フォレスト拡張機能がインストールされておらず、何らかの理由でインストールしたくない場合、これを行うことができます。
$ hg clone http://hg.openjdk.java.net/jdk7/jdk7 && jdk7/get_source.sh
別のオプションは、完全なオフサイトバンドルをダウンロードすることです。 これはいくつかのコーナーをカットするのに役立ちますが、バージョン管理システムを使用する魅力を奪います。
興味深い機能:何らかの理由で、jaxpとjaxwsは別のリポジトリに格納されます。 したがって、適切なサイト( jaxp.java.netおよびjax-ws.java.net )からそれらを手動でダウンロードするか、
ALLOW_DOWNLOADS=true
と
ALLOW_DOWNLOADS=true
して
ALLOW_DOWNLOADS=true
が必要なものすべて
make
ダウンロードできる
make
する必要があります。 個人的には、このオプションは私にとってより便利なようです。 そうそう、完全なソースバンドルでは、すべてが既にダウンロードされています。
皿を調理できない道具
明確なビジネス、組み立てには多くのすべてが必要になります。 最も簡単なのは、少なくともバージョン1.6のブートストラップjdkです。 変数
ALT_BOOTDIR
を使用して、パスを指定する必要があります。 さらに、明らかな
ant
やmakeからCUPSやALSAまで、すべての巨大な山が必要です。 正確にすべてを保持する最も簡単な方法は、パッケージマネージャーにすべてのビルド依存関係を満たすよう依頼することです。 たとえば、aptitudeを使用する場合:
$ aptitude build-dep openjdk-6
何が起こっているのかを確認する
必要なものがすべて揃っていることを確認するには、sanity
目標に
make
を実行
make
必要があります。 環境変数の設定に注意してください。
$ LANG=C ALT_BOOTDIR=/usr/lib/jvm/java-6-openjdk make sanity
すべてが順調であれば、碑文の
Sanity check passed
すべてが悪い場合、かなりわかりやすいエラーメッセージが表示されます。 それを修正して再試行してください。
これで、jdk自体をビルドできます。 環境変数は以前に
ALLOW_DOWNLOADS
追加し
ALLOW_DOWNLOADS
。
$ ALLOW_DOWNLOADS=true LANG=C ALT_BOOTDIR=/usr/lib/jvm/java-6-openjdk make
20〜40分で成功した場合は、次の形式のメッセージを受け取ります。
#-- Build times ---------- Target all_product_build Start 2012-04-20 01:56:53 End 2012-04-20 02:02:14 00:00:06 corba 00:00:09 hotspot 00:00:06 jaxp 00:00:08 jaxws 00:04:47 jdk 00:00:05 langtools 00:05:21 TOTAL
有用なものが実際に収集されたことを確認して、次のステップに進むことができます。
$ ./build/linux-amd64/bin/java -version openjdk version "1.7.0-vasily_p00pkin" OpenJDK Runtime Environment (build 1.7.0-vasily_p00pkin-gs_2012_04_20_01_06-b00) OpenJDK 64-Bit Server VM (build 23.0-b21, mixed mode)
別のオペレーティングシステムを使用しています...
... BSDベース
ここではそれほど悪くはありません。 オラクルの優秀なスタッフの厳しい指導の下、私はサプサンの前庭にあるMacbookにホットスポットを組み立てることができました。 しかし、翌夜のJDK全体は実際にはうまくいきませんでした。 ただし、これを行うことができます。新しいXcodeと多くの忍耐が必要です。 私はどちらも持っていなかったので、Selectelクラウドでより強力なマシンを起動し、実験を行ったところです。 ボーナスとして、ラップトップをロードしていない間、クラウドでのアセンブリが高速であるため、この時点で(剣で戦う代わりに、椅子に乗って)何か役に立つことができます。 それでもポピーで収集したい場合は、プロセスの説明を以下に示します。
...まあ、あなたはそれを知っていますか?
実際、ここでもすべてがそれほど悪いわけではありません。 シグウィンとスモークマナで武装してください。
最も興味深いの始まり
-患者、あなたは倒錯に苦しんでいますか?-あなたは何ですか、博士! 私はそれらを楽しみます!
ここで、ガベージコレクターを切り取るためにソース内のどこを呼び出す必要があるかを理解するタスクに直面しています。 これを行うには、3つの明白な方法があります。知っている人に尋ねる、すべてのソースコードを読む、またはunningで機知に富むことを示す。 最初の方法はうまくいきませんでした。知識のある人々は私を奇妙に見つめ、そのような疑わしい行動に参加することを拒否して去ったからです。 2番目の方法は、イデオロギーの観点からは最も正確でしたが、当時は残念でした。 したがって、3番目の方法は残りました。
論理的に推論してみましょう。外部からガベージコレクターにどのように影響を与えることができますか? 起動時にキーを使用する(
-XX:+UseParallelGC
)と
System.gc()
を使用するという2つのパスがすぐに思い浮かびます。 前者はより論理的に見えますが、私は後者から始めることにしました。なぜなら、javadocsはそこで何が起こっているのかについての関心を完全に満たすことができないからです。 Javaソースでは、この呼び出しはランタイムに委任され、メソッドは既にネイティブです。 JNIを使用したことがある人なら誰でも、関数名がネイティブコード
Java_java_lang_Runtime_gc
コンパイルされる方法を知っています。 Quick
grep
は
jdk/src/share/native/java/lang/Runtime.c
にそのようなコードを作成し
jdk/src/share/native/java/lang/Runtime.c
。ここでは、次の行に興味があります。
| |
JVM_GC
、
JVM_GC
探してい
JVM_GC
。 彼の広告は
src/share/vm/prims/jvm.cpp
ます:
| |
DisableExplicitGC
と、
Universe::heap()
collect
メソッドです。 それがいかに簡単か:
System.gc()
はコレクターが同期的に開始することだけを行うことが判明しました。 ドラマはありません。 えー さて、何もありませんが、
collect()
メソッドでアセンブリを無効にできる可能性が高いことがわかりました。 ファイル
hotspot/src/share/vm/memory/universe.hpp
でUniverseクラスを簡単に発見し、静的
heap
メソッドが
CollectedHeap*
返し、
initialize_heap()
メソッドが存在することを確認し
initialize_heap()
宇宙をテーマにした少し叙情的な余談
OpenJDKのコードの品質は優れていると言わざるを得ません。優れた構造、何が起きているのか、多くのコメントを理解するのは簡単です。 たとえば、ここに素晴らしいスニペットがあります:
121 122 123 124 125 126 127
class Universe: AllStatic { // Ugh. Universe is much too friendly. friend class MarkSweep; friend class oopDesc; // friend' //... }
さて、コレクターに戻りましょう。
initialize_heap()
メソッドはヒープを作成し、ユーザーが指定したコレクターに応じて、特定の実装が選択されます。 完全なリストは、
hotspot/src/share/vm/gc_interface/collectedHeap.hpp
ファイルにあります。
| |
クラスの学習を続けると、最終的に必要なコードが見つかります。
| |
ここでは、コメントが最も役立ちます。 英語を十分に知らない人のために、私は説明します:最初のメソッド、単に
collect()
、「外部から」(たとえば、
System.gc
から、または同じ
grep
示すように、Linuxのメモリ割り当てが失敗した場合)組み立てられることを意図しています 2つ目は、ガベージコレクションを担当する仮想マシンスレッドから起動されます(必要なすべてのロックが既に保持されていることを前提としています)。 簡単な解決策がすぐに思い浮かびます。これらのメソッドが呼び出されたときに、アセンブリが発生しないようにするためです。 私はこのアプローチを初めて試しましたが、それは不運です。すべてが多少複雑であり、各ヒープ実装にはアセンブリが行われる独自の追加の場所があることがわかりました。 そのため、特定の実装(最も単純なものとして
GenCollectedHeap
を
MarkSweepPolicy
した
MarkSweepPolicy
)を選択する必要があり、フラグ(これを
UseTheForce
と呼び
UseTheForce
)に応じて、何もせずにアセンブリを生成するメソッドを終了する必要がありました。 その結果、最初のバージョンの変更は次のようになりました 。
やってみます!
ガベージコレクターの通常の操作中にOOMをスローしないクラスを簡単に説明しますが、クラスが存在しない場合、これは大きな喜びで実行されます。
| |
$ ./build/linux-amd64/bin/java -XX:+UseTheForce -verbose:gc TheForceTester Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at ru.yandex.holocron.core.TheForceTester.main(TheForceTester.java:10)
やった! 激しいワイン! さらに、テスターアプリケーションでは、現在の空き領域の出力を追加し、他のすべても正常に動作しているように確認できます:
Xmx != Xms
ヒープが拡張し、等しい場合、空き領域は理論上必要なだけ正確に減少します。 クラス! スプーン1杯分のタールを追加するだけです。
免責事項と同じ質問への回答
その同じ質問によって、私は、もちろん、「そしてなぜ?!」を意味します。 トピックの冒頭で、パフォーマンスが非常に重要なDisruptorについて説明しました。 ご存知のように、ガベージコレクターでは、アプリケーションに予測不可能な遅延が発生します。 したがって、ほとんどのオブジェクトを再利用し、時々再起動することが可能であれば、GCを飲みました-これは非常に高速な方法です。
また、できるかどうかを見たいからです。 また好奇心が強い。
免責事項は次のとおりです。指定されたソリューションはかなり汚れており、概念実証としての役割を果たします。 まず、実際にガベージコレクションを瞬時に作成したため、仮想マシンでコレクターを使用することで他のさまざまなオーバーヘッドが発生しました。 良い意味で、これらのオーバーヘッドを完全に排除する
CollectedHeap
実装を書く価値がありました。 ただし、この後でも、たぶんいくつかの場所を探し回る必要があります。
このすべてから何が続きますか? さらにトピックをお待ちください! :)
PS他に何をしますか?
linux-amd64アーカイブの下でコンパイルされたPPS: clck.ru/1-L-9 (Yandex.Disk)
PPPSリポジトリ全体を複製しないでください。 600メガバイト以上の重量があり、ホストされているマシンのトラフィックは有料です。 ただし、これは、java.netに傾いてから、単一のコミット( 3358:3f014511ecce )をプルすることを妨げるものではありません。