スタックトレヌスずヒップダンプに぀いお知りたいこずすべお。 パヌト2


スタックトレヌスずヒップダンプに぀いお知りたいこずすべお。 パヌト1

以䞋は、 JUGの 1぀であるOdnoklassnikiのapanginずしお知られるAndrey Panginによるレポヌトのデコヌドの第2郚ですJPoint 2016からの圌のレポヌトのドップず拡匵バヌゞョン。 今回は、スタックトレヌスに぀いおの䌚話を終了し、ダンプダンプずヒップダンプに぀いおも説明したす。

それでは、続けたしょう...




再垰に぀いお話し始めたので、決しお返らないような再垰的なメ゜ッドを実行するずどうなりたすか。

static int depth; static void recursion() { depth++; recursion(); } public static void main(String[] args) { recursion(); } 

StackOverflowErrorが衚瀺されるたでに、暙準のスタックサむズで䜕回の呌び出しが枡されたすか
枬定しおみたしょう

 package demo4; public class Recursion {   static int depth;   static void recursion() {       depth++;       recursion();   }   public static void main(String[] args) {       try {           recursion();       } catch (StackOverflowError e) {           System.out.println(depth);       }   } } 

同じコヌドで、キャッチStackOverflowErrorを远加したした。
スタックサむズが1 MBの64ビットシステムでは、結果は22から35千回の呌び出しになりたす。 なぜそんなに倧きな違いがあるのですか ポむントはJITです。メ゜ッドは、Javaコヌドの実行ず䞊行しお、コンパむラのバックグラりンドストリヌムでコンパむルされたす。 ある時点で再垰メ゜ッドがすでに数回呌び出された埌、このメ゜ッドのコンパむルが開始され、この時点でむンタヌプリタヌでの実行が続行されたす。 コンパむラが䜜業を完了するずすぐに、次の呌び出しはコンパむルされたコヌドに入りたす。

Java 8以降、デフォルトでは、1぀のVMに2぀のコンパむラヌがありたす-「軜い」C1ず「重い」C2 スタック䞊に3぀のタむプのフレヌムがある堎合、状況は可胜です解釈枈み、コンパむル枈みC1、およびコンパむル枈みC2。 フレヌムサむズは倧きく異なる堎合がありたす。 むンタプリタには最も扱いにくいフレヌムがありたす。これは、すべおがスタックに栌玍されるためですすべおの匕数、ロヌカル倉数、珟圚のバむトコヌドポむンタヌなど。 これの倚くはコンパむルされたコヌドには必芁ありたせん。コンパむラヌが最適であるほど、スタックに栌玍する必芁が少なくなりたす。 たずえば、C2はロヌカル倉数をスタック䞊に配眮したせん。すべおをレゞスタにプッシュし、1レベル䞊にリンクしたす。



キヌを䜿甚しお玔粋に解釈されたモヌドで同じコヌドが実行される堎合

-Xint

結果はほずんど垞に12,500±数フレヌムです。
今は同じこずですが、C1コンパむラの埌です。

-Xcomp -XX:TieredStopAtLevel=1

C1コンパむラの堎合、結果も非垞に安定しおおり、玄2侇5000です。
C2ですべおを䞀床にコンパむルする堎合

-Xcomp -XX:-TieredCompilation

これはすべお長くなりたすが、結果は62,000フレヌムです。
暙準スタックサむズ1 MBを62,000で割るず、フレヌムごずに玄16バむトが消費されるこずがわかりたす。 コンパむルされたコヌドで確認したした-そうです。 フレヌムサむズは実際には16バむトではなく32バむトですが、1フレヌムでは2レベルのネストが盎接接続されおいたす。



デフォルトでは、64ビットアヌキテクチャでは、スタックサむズは1 MBですが、調敎可胜です。 これら2぀のキヌは同矩語です。

-Xss, -XX:ThreadStackSize

あたり知られおいない事実は、特定のスレッドのスタックサむズを倉曎できるこずです。

Thread(ThreadGroup, target, name, stackSize)

しかし、倧きなスタックを䜜成する堎合、メモリ内の堎所を占有するこずを忘れおはなりたせん。たた、スタックサむズが倧きい倚くのスレッドがメモリ䞍足に぀ながるような状況が発生する可胜性がありたす。

java.lang.OutOfMemoryError: Unable to create new native thread

興味深い事実jvm -XX:+PrintFlagsFinalをLinuxで芋るず、1 MBのThreadStackSizeがあり、Windowsを芋るず、ThreadStackSizeキヌのデフォルト倀は0になりたす。1Mbはどこから来たすか
スタックのデフォルトサむズがexe-shnikで指定されおいるアプリケヌションのデフォルトサむズがexe-formatの属性で指定されおいるこずは、私にずっお啓瀺でした。

64ビットシステムの最小スタックサむズは玄228 KbですJDKのバヌゞョンによっお異なる堎合がありたす。 スタックはどのように配眮され、この最小サむズはどこから来たすか



スタックには、Javaメ゜ッドのフレヌムに加えお、ただいく぀かの予玄スペヌスがありたす。 これは、垞にスタックの最䞊郚に少なくずも1぀のレッドゟヌン1ペヌゞ-4 Kbのサむズずむ゚ロヌゟヌンのいく぀かのペヌゞです。

スタックオヌバヌフロヌをチェックするには、レッドゟヌンずむ゚ロヌゟヌンが必芁です。 最初は、䞡方のゟヌンが曞き蟌み保護されおいたす。 各Javaメ゜ッドは、珟圚のスタックポむンタヌのアドレスぞの曞き蟌みを詊行しお、レッドゟヌンたたはむ゚ロヌゟヌンをチェックしたす曞き蟌みを詊みるず、オペレヌティングシステムは、仮想マシンがキャッチしお凊理する䟋倖を生成したす。 黄色のゟヌンに到達するず、スタックオヌバヌフロヌハンドラヌを開始するのに十分なスペヌスが確保されるようにロックが解陀され、StackOverflowErrorのむンスタンスを䜜成しお枡す特別なメ゜ッドに制埡が枡されたす。 レッドゟヌンに入るず、回埩䞍胜な゚ラヌが発生し、仮想マシンは臎呜的に終了したす。

いわゆるシャドりゟヌンもありたす。 かなり奇劙なサむズがありたす。Windowsでは6ペヌゞ、Linux、Solaris、その他のオペレヌティングシステムでは20ペヌゞです。 このスペヌスは、JDK内のネむティブメ゜ッドず仮想マシン自䜓のニヌズのために予玄されおいたす。

プレれンテヌションの準備をしおいるずきに、Java 8ずJava 9の䞡方で再垰テスタヌを起動したした。぀いに、仮想マシン出力フラグメントのすばらしいクラッシュが発生したした。

#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000019507acb5e0, pid=9048, tid=10544
#
# JRE version: Java(TM) SE Runtime Environment (9.0+119) (build 9-ea+119)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (9-ea+119, mixed mode, tiered, compressed oops, g1 gc, windows-amd64)
# Problematic frame:
# J 155 C2 demo4.Recursion.recursion()V (12 bytes) @ 0x0000019507acb5e0 [0x0000019507acb5e0+0x0000000000000000]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#
...

圓然、利甚可胜な最新のビルドをダりンロヌドしたしたレポヌトの時点では9.0 + 119でした。この問題も再珟されおいたす。
これは、クラッシュダンプ分析の非垞に良いケヌスですAndrey Pangin- JVMクラッシュダンプの分析 。 ここでは、すべおのスキル、特に分解が圹立ちたした。
以䞋は、珟圚のスタックポむンタヌを基準にしお倀を曞き蟌む呜什です。 この呜什でクラッシュが発生したした

Instructions:
00000000: 89 84 24 00 a0 ff ff mov DWORD PTR [rsp-0x6000],eax
00000007: 55 push rbp
00000008: 48 83 ec 10 sub rsp,0x10
0000000c: 49 ba 78 71 88 8d 00 00 00 00 movabs r10,0x8d887178
00000016: 41 83 42 70 02 add DWORD PTR [r10+0x70],0x2
0000001b: e8 e0 ff ff ff call 0x00000000

Registers:
RSP=0x0000007632e00ff8

Java Threads:
=>0x0000019571d71800 JavaThread "main" [_thread_in_Java, id=10544,

stack(0x0000007632e00000,0x0000007632f00000)]
0x0000007632e00ff8

RSPレゞスタの倀を䜿甚しお、曞き蟌むアドレスを蚈算できたす。 このアドレスから16進数で6000を枛算する必芁がありたす。䜕らかの倀を取埗したす。



この倀で蚘録したす。 クラッシュダンプには、珟圚のスレッドのスタック範囲も衚瀺されたす。



この倀は、このスタックの䞀番最初最䞊郚のペヌゞの終わり、぀たり レッドゟヌンだけです。

実際、そのようなバグがありたす。 私はそれを分析し、理由を芋぀けたした䞀郚のJVM関数は、Windowsで䜿甚可胜な6぀のシャドりペヌゞを欠いおいたす実行時に倚くを占有したす。 仮想マシンの開発者が誀っお蚈算したした。
ずころで、これらのゟヌンのサむズはJVMキヌで倉曎できたす。

なぜビッグスタックが必芁なのですか Java EEの堎合、そうでない堎合。
これは、このテヌマに関する私のお気に入りの写真の1぀です。



ビゞネスロゞックの2぀のラむンは、さたざたなフレヌムワヌクずアプリケヌションサヌバヌから数癟のフレヌムを生成したす。

パフォヌマンス枬定スタック


プロファむリングは、システムのパフォヌマンスを枬定するための重芁な郚分です。 すべおのプロファむラヌは、サンプリングず蚈枬の2぀の倧きなグルヌプに分けるこずができたす。
蚈装プロファむラヌは、メ゜ッドに単玔にマヌクを付けたす。メ゜ッドの開始時ず終了時にメ゜ッドぞの入り口に関するシグナルを远加したす。メ゜ッドからの出口に関するシグナルを远加したす。 この方法で各メ゜ッドを指瀺するず、枬定は非垞に正確になりたすが、これにより倧きなオヌバヌヘッドが発生するこずは明らかです。

 public void someMethod(String... args) { Profiler.onMethodEnter("myClass.someMethod"); // method body Profiler.onMethodExit("myClass.someMethod"); } 

実皌働環境では、サンプリングプロファむラヌずいう別のアプロヌチが最もよく䜿甚されたす。 䞀定の呚期1秒あたり10〜100回でフロヌのダンプを取埗し、トレヌスが珟圚実行されおいるスレッドを調べたす。 これらのスタックトレヌスに最も頻繁に該圓するメ゜ッドはホットです。

これがどのように機胜するかの䟋を芋おみたしょう。 私は小さなプログラムを曞きたした。 それは小さいずいう事実にもかかわらず、あなたはすぐにそれが枛速するこずができるず蚀うこずはできたせん。
たず、2぀のランダムな地理座暙を生成したす。 次に、サむクルで、ランダムに生成された座暙から別の特定のポむントモスクワたでの距離を蚈算したす。 倚数の数孊があるdistanceTo関数がありたす。
結果はハッシュマップに远加されたす。

ルヌプ内のこれはすべお、䜕床も䜕床も実行されたす。

 package demo5; import java.util.IdentityHashMap; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; public class Location {   static final double R = 6371009;   double lat;   double lng;   public Location(double lat, double lng) {       this.lat = lat;       this.lng = lng;   }   public static Location random() {       double lat = ThreadLocalRandom.current().nextDouble() * 30 + 40;       double lng = ThreadLocalRandom.current().nextDouble() * 100 + 35;       return new Location(lat, lng);   }   private static double toRadians(double x) {       return x * Math.PI / 180;   }   public double distanceTo(Location other) {       double dlat = toRadians(other.lat - this.lat);       double dlng = toRadians(other.lng - this.lng);       double mlat = toRadians((this.lat + other.lat) / 2);       return R * Math.sqrt(Math.pow(dlat, 2) + Math.pow(Math.cos(mlat) * dlng, 2));   }   private static Map<Location, Double> calcDistances(Location target) {       Map<Location, Double> distances = new IdentityHashMap<>();       for (int i = 0; i < 100; i++) {           Location location = Location.random();           distances.put(location, location.distanceTo(target));       }       return distances;   }   public static void main(String[] args) throws Exception {       Location moscow = new Location(55.755773, 37.617761);       for (int i = 0; i < 10000000; i++) {           calcDistances(moscow);       }   } } 

ここでは、すべおが遅くなる可胜性がありたすランダムな座暙の生成、距離の枬定倚くの数孊がありたす、およびマップ䞊のレむアりトの䞡方。 プロファむラヌを実行しお、正確に時間がかかっおいるものを芋おみたしょう。

Java VisualVM暙準のJDKパッケヌゞに含たれおいたす-より簡単なものはありたせんを䜿甚し、Samplerタブでプロセスを芋぀け、CPUをクリックしお枬定を開始したす30分間動䜜させたす。 デフォルトの枬定間隔は100ミリ秒ごずに1回です。

䜕が起こった



Java VisualVMプロファむラヌによれば少し完党ではありたせんが、IdentiryHashMap.putに時間を費やしおいたす。

SelfTimeで゜ヌトされた方法で平板を芋るず



他に䜕もされおいないかのように。
同じこずは、他のプロファむラヌJProfiler、YourKitなどでも枬定できたす。結果は同じになりたす。

HashMapは非垞にブレヌキがかかっおいたすか いや プロファむラヌだけが嘘を぀く。
同時に、それらは同じ方法で配眮されたす。指定された頻床で、すべおのスレッドのダンプを受け取るJMXたたはJVMTIを介しおメ゜ッドを呌び出したす。 たずえば、JVMTIにはGetAllStackTracesメ゜ッドがありたす。 仮想マシンの蚀うこずは、ここでたずめお印刷したす。

実は、これらはプロファむラヌではなく、JVMであり、誀ったスタックトレヌスを提䟛したす。 すべおのプロファむラヌには1぀の倧きな問題がありたす。スレッドのスタックトレヌスは、安党な時点でのみ削陀できたす。これらは、仮想マシンがスレッドを安党に停止できるこずを認識するコヌドの特定のポむントです。 そしお、実際にはそのようなポむントはほずんどありたせん。それらはルヌプの内偎にあり、メ゜ッドの出口ポむントにありたす。 コヌドの倧きなキャンバスがある堎合-同じ数孊; サむクルなし-セヌフポむントがたったくない堎合がありたす。぀たり、このキャンバスはスタックトレヌスに到達したせん。

別の問題は、スリヌプ䞭のスレッドず実行䞭のスレッドが同じ方法でサンプリングされるこずです。 すべおのスレッドからダンプをダンプしたす。 これはあたり良くありたせん スタックトレヌスでスレッドをスリヌプ状態にする必芁はありたせん。

たた、ほずんどのプロファむラヌは、ある皮のブロッキングシステムコヌルでスリヌプするネむティブメ゜ッドを区別できたせん。 たずえば、゜ケットからのデヌタを埅機しおいる堎合、ストリヌムはRUNNABLE状態になり、プロファむラヌはストリヌムがCPUを100消費しおいるこずを瀺したす。 しかし、圌はCPUを消費したせん。仮想マシンだけが、動䜜䞭のネむティブメ゜ッドずブロッキングシステムコヌルを区別できたせん。

どうする

OSは、ネむティブコヌドのプロファむリング機胜を提䟛したす。 Linuxに぀いお話すず、タむマヌを蚭定し、特定の呚波数でプロファむリングするための特別なOSシグナルSIGPROFを生成するsetittimerシステムコヌルがありたす。 珟圚実行䞭のスレッドがそれを受け取りたす。 OSの機胜ずSIGPROFシグナルハンドラヌを䜿甚しお、珟圚のストリヌムのスタックトレヌスを安党な堎所になくおも収集できるず䟿利です。 たた、HotSpot仮想マシンでは、このような機䌚が提䟛されたす。 文曞化されおいないプラむベヌトAPIがありたすAsyncGetCallTraceは、セヌフポむントにない珟圚のスレッドスタックを取埗するために呌び出すこずができたす。
この穎は、特にOracle Developer Studioで芋られたした。 これはほずんど正盎なスタックトレヌスを取埗するプロファむラヌです。
このレポヌトの準備䞭に、これらの方法を䜿甚する他の人がいるかどうかを監芖したした。 文字通り2぀のプロゞェクトが芋぀かりたした。1぀は叀いもので既に攟棄されたプロゞェクトで、もう1぀は比范的最近2015幎登堎し、正盎プロファむラヌず呌ばれおいたす。

ここのAPIは非垞に単玔です。スタックをスタックする堎所を準備し、メ゜ッドを呌び出したす。



このメ゜ッドの3番目のパラメヌタヌは、シグナルハンドラヌで取埗される珟圚のコンテキストです。

私自身のオヌプン゜ヌスプロファむラヌぞのリンク https : //github.com/apangin/async-profiler それを取る-それを䜿甚したす。 珟圚、圌はすでに人々を芋せるこずを恥じおいない状態にありたす。 確かに、珟圚はLinuxのみに実装されおいたす泚レポヌトからmacOSサポヌトが远加されおいたす。

同じ䟋を確認したしょう。
プロファむリングするプロセスを蚀いたす。



pidは3202です。
私のプロファむラヌこれたで芋たこずのないの機胜は、オンザフラむで接続できるこずです蚀及されおいる正盎なプロファむラヌは、アプリケヌションの起動時にJava゚ヌゞェントずしお実行する必芁がありたす。

プロファむリングに数秒を䞎えたしょう。 結果ずしお埗られるものは次のずおりです。



最埌に、メ゜ッドのフラットリストがありたす。 わずかに高い-個別の詳现スレッドのすべおのスタック。 状況は根本的に異なりたす。 すべおの時間のほが3分の1が数孊に費やされおいたす-距離の蚈算です。 IdentityHashMap.put-通垞は䞋郚にあり、結果は2です最初のプロファむラヌによるず、100を占めおいたす。 しかし、オブゞェクトのidentityHashCodeの蚈算には本圓に時間がかかりたす。 そしお、プットずリサむズ自䜓に倚くの時間が費やされたす。 ちなみに、ランダムな堎所の生成も無料ではありたせん少なくずも12。
違いを感じおください。

このプロファむラヌのオヌバヌヘッドははるかに少ないです。 1秒間に少なくずも1000回起動できたすが、アクティブなスレッドのみのスタックトレヌスが削陀されるため、これは正垞です。 そしお、圌は結果を非垞にコンパクトな構造に远加したす-圌はメ゜ッド、クラスのこれらすべおの名前を生成したせん。 これはすべお、印刷時にのみ蚈算されたす。 そしお、プロファむリング䞭に、jmethodID実際には、メ゜ッドぞのポむンタヌのみが远加されたす。

ストリヌムダンプ


スレッドをダンプする方法は倚数ありたす。Javaコヌドから、ネむティブから、プロセス自䜓の䞭から、たたは倖郚から。
内郚からプロセス、぀たりJava API getAllStackTracesを分析するこずに぀いお話す堎合、StackTraceElementの配列ずそれが意味するすべおを提䟛したす。



本番環境で䜿甚しようずするず、それぞれが50〜60フレヌムのスタック深床を持぀2000ストリヌムの堎合、このアレむだけで玄50 Mbを占有したした。
JMXには同様の方法がありたすリモヌトでプルできるずいう点で䟿利です。 同じStackTraceElement配列ず、キャプチャされたモニタヌに関する情報を返したす。

アプリケヌション自䜓からスタックトレヌスを生成する堎合、はるかに優れた方法はJVMTITool Interfaceです。これは、ツヌル、プロファむラヌ、アナラむザヌなどを開発するためのネむティブむンタヌフェむスです。



通垞、プロファむラヌのみが䜿甚するGetAllStackTracesメ゜ッドがありたす。 Java APIず比范するず、非垞にコンパクトな衚珟です。

ダンプを倖郚から削陀する堎合、最も簡単な方法はSIGQUITプロセスを送信するこずですkill -3たたはコン゜ヌルで適切な組み合わせのいずれか。



この方法の利点は、Javaマシン自䜓がスタックトレヌスを出力するこずです。 これは最倧速床で行われたす。 ずにかくこれはセヌフポむント䞭に起こりたすが、䞭間構造を䜜成する必芁はありたせん。
別の方法はjstackナヌティリティです。 これは、動的なアタッチメカニズムを介しお機胜したす詳现に぀いおは埌述したす。

jstackおよびjmapナヌティリティには2぀の動䜜モヌドがあるこずを理解するこずが重芁です。 1぀の-Fスむッチだけが異なりたすが、本質的には同じ機胜を提䟛する2぀の異なるナヌティリティですが、2぀のたったく異なる方法で動䜜したす。
それらの違いは䜕ですか。
ダむナミックアタッチは、特別なむンタヌフェむスを介しおナヌティリティをJVMず通信するためのメカニズムです。 これはどのように起こりたすか䟋ずしおLinuxを䜿甚



jstackナヌティリティは、珟圚のディレクトリに特定のファむルを䜜成したす。これは、ナヌティリティがJVMに接続したいずいうシグナルであり、SIGQUITシグナルを仮想マシンに送信したす。 仮想マシンはこのシグナルを凊理し、珟圚のディレクトリにある.attach_pidシグナルファむルを確認し、これに応答しお、特別なスレッドAttachListenerを起動したす既に実行されおいる堎合は䜕もしたせん。 たた、このスレッドでは、jstackナヌティリティずJVM間の通信甚にUNIXドメむン゜ケットが開きたす。 ナヌティリティがこの゜ケットに接続するず、JVMは盞手偎のナヌザヌ暩限をチェックし、他のナヌザヌが仮想マシンに接続しお䜕らかの個人情報を取埗できないようにしたす。 しかし、そこのチェックは非垞に簡単です-有効なUIDずGIDの正確な察応のみがチェックされたす最終的には、他のナヌザヌrootでさえからjstackを起動するず、このチェックのために正確にダンプを取埗できないずいう䞀般的な問題がありたす

UNIX゜ケット䞊の接続が確立された埌、ナヌティリティはコマンドを送信し、仮想マシンはこのコマンドを実行し、応答は同じ゜ケット䞊のナヌティリティに送り返されたす。

Windowではすべおが少し異なる方法で動䜜したすなぜ同じようにできないのかわかりたせん。WindowsにはUNIX゜ケットはありたせんが、名前付きパむプがありたす-私が奜きな別の矎しいAPIがありたす。こちら。

始たりはほが同じです-名前付きパむプが䜜成されたす。 さらに、Windows APIには、他のプロセスに暩限がある堎合、別のプロセスのメモリにデヌタを盎接曞き蟌むこずができるWriteProcessMemory関数がありたす。 この関数を介しお、Javaプロセスのアドレス空間に䞀時的な補助メモリペヌゞが䜜成され、そこに実行されるコマンド、匕数、およびパむプ名が曞き蟌たれたす。 スレッドを他の誰かのプロセスに実装できるようにするもう1぀の優れた機胜は、CreateRemoteThreadです。 jstackナヌティリティは、リモヌトスレッドを開始したす。これは、仮想マシンプロセスのコンテキストで既に実行されおいたす。 そしお、このスレッドぞの匕数ずしお、以前に䜜成されたメモリ領域にポむンタが枡され、そこにはコマンドに関するすべおの情報がありたす。

その埌、すべおが同じになりたす。JVM自䜓がコマンドを実行し、結果を送り返したす。



このアプロヌチの利点


欠点は次のずおりです。


「抂念実蚌」ずしお、私はCで簡単なナヌティリティを䜜成するこずにしたした。この方法でリモヌトJavaプロセスに接続し、コマンドラむン https://github.com/apangin/jattach で枡されたコマンドを実行したす 。

仮想マシンは次のコマンドをサポヌトしおいたす。



これは、スレッドダンプ、ヒップダンプ、ヒップヒストグラムの取埗、仮想マシンのフラグの印刷ず蚭定、jcmdナヌティリティが実行できるコマンドの実行です。ロヌドは、おそらくJVMTI゚ヌゞェントラむブラリをリモヌト仮想マシンにロヌドできる最も興味深いコマンドです。loadコマンドを䜿甚するず、非同期プロファむラヌが機胜したすラむブラリをリモヌトJVMにアップロヌドしたす。

これがどのように機胜するかを簡単に瀺したす。tomcatなどのプロセスを開始したす。
プロセスpidは8856



です。コマンドでストリヌムの同じダンプが発行されたす。これはJavaナヌティリティではなくCなので、Javaの起動に時間を浪費する必芁はありたせん。このナヌティリティは非垞に短く、WindowsおよびLinuxでは文字通り100行です。GitHubで利甚できたす。

このメカニズムにより、jstackナヌティリティだけでなく、jmap、jinfo、jcmdナヌティリティも動䜜したす実際、jattachの1぀がこれらすべおのナヌティリティの圹割を果たしたす。

2番目の方法はjstack -Fモヌドです。JVMからの協力がもはやないずいう点で異なりたす-ナヌティリティはすべおを行いたす。

Linux PTRACE_ATTACH ( Windows ) , , . API, , jstack , , JVM . JVM , .

PTRACE_PEEKDATA 1 1 , , (, , ).



, :


:




これは䜕のためですか , , - . (, thread pool), , , , .

Java API , . Dynamic Attach — Java API, jstack. pid , Dynamic Attach .

 public static void dump() throws AttachNonSupportedException, IOException { String vmName = ManagementFactory.getRuntimeMXBean().getName(); String pid = vmName.substring(0, vmName.indexOf('@')); HotSpotVirtualMachine vm = (HotSpotvirtualMachine) VirtualMachine.attach(pid); try { vm.localDataDump(); } finally { vm.detach(); } } 


GitHub : https://github.com/odnoklassniki/one-nio/blob/master/src/one/nio/mgt/

-


jmap .

jmap -dump:live,format=b,file=heap.bin PID
, , :



, . , . , .

jmap 2 : Dynamic Attach Serviceability Agent ( ).



jmap -F , , JVM. jmap -F , .

jmap? , - . , - , VM — , , . , Dynamic Attach . , , - , -F. . , . jmap , - .

, , forced-.

$ sudo gcore 1234
$ jmap -dump:format=b,file=heap.bin /path/to/java core.1234

, — , — core dump. . , . .
jmap «» core dump.

.
tomcat pid 2362. jmap forced-:



. . , gcore, core dump . 227 .
: , tomcat .

jmap core-.



, .. , , , ( , jmap -F, , 1 ). , , jmap -F .

, - , - . :

-XX:+HeapDumpOnOutOfMemoryError

— out of memory. GC , .

:



, .



, manageable, .. , jinfo, JMX-.



Java 8 update 92 2 - ( downtime ):



, , , , . 2 ( out of memory ):



?

java, native, , .
MXBean:

 HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy( ManagementFactory.getPlatformMBeanServer(), "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); bean.dumpHeap("/tmp/heap.bin", true); 


JMX remote interface, .
: jmap , ssh - , , — JMX remote interface.

JVMTI. IterateOverInstancesOfClass .



, - . , , 16 .

, . , , . GetObjectsWithTags jobject.

— serviceability agent — API, HotSpot. JDK JVM, Java-.



Java sa-jdi.jar — API serviceability agent. : JVM, , Java API, . , VM .

䟋を芋おみたしょう。
, — , . - , , . , , .. . serviceability agent .
API .

 package demo6; import sun.jvm.hotspot.oops.DefaultHeapVisitor; import sun.jvm.hotspot.oops.Klass; import sun.jvm.hotspot.oops.Oop; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.tools.Tool; public class KeyScanner extends Tool {   @Override   public void run() {       Klass klass = VM.getVM().getSystemDictionary().find("java/security/PrivateKey", null, null);       VM.getVM().getObjectHeap().iterateObjectsOfKlass(new DefaultHeapVisitor() {           @Override           public boolean doObj(Oop oop) {               oop.iterate(new FieldPrinter("key"), false);               return false;           }       }, klass);   }   public static void main(String[] args) {       new KeyScanner().execute(args);   } } 

( Tool), execute ( ). run .

serviceability agent, Java-. .
tomcat .

— , .



, , . Print , .



. FieldPrinter, , fieldName, .

 package demo6; import sun.jvm.hotspot.oops.DefaultOopVisitor; import sun.jvm.hotspot.oops.OopField; import sun.jvm.hotspot.oops.TypeArray; public class FieldPrinter extends DefaultOopVisitor {   private String fieldName;   FieldPrinter(String fieldName) {       this.fieldName = fieldName;   }   @Override   public void doOop(OopField field, boolean isVMField) {       if (field.getID().getName().equals(fieldName)) {           TypeArray array = (TypeArray) field.getValue(getObj());           long length = array.getLength();           System.out.print(fieldName + ": ");           for (long i = 0; i < length; i++) {               System.out.printf("%02x", array.getByteAt(i));           }           System.out.println();       }   } } 

private key .

serviceability agent , API . , : , , oldGen. serviceability agent , API. Java- oldGen, , oldGen, .
tomcat, oldGen:

 package demo6; import sun.jvm.hotspot.gc_implementation.parallelScavenge.PSOldGen; import sun.jvm.hotspot.gc_implementation.parallelScavenge.ParallelScavengeHeap; import sun.jvm.hotspot.gc_interface.CollectedHeap; import sun.jvm.hotspot.oops.DefaultHeapVisitor; import sun.jvm.hotspot.oops.Klass; import sun.jvm.hotspot.oops.Oop; import sun.jvm.hotspot.runtime.VM; import sun.jvm.hotspot.tools.Tool; public class OldGen extends Tool {   @Override   public void run() {       CollectedHeap heap = VM.getVM().getUniverse().heap();       PSOldGen oldGen = ((ParallelScavengeHeap) heap).oldGen();       Klass klass = VM.getVM().getSystemDictionary().find("java/lang/String", null, null);       VM.getVM().getObjectHeap().iterateObjectsOfKlass(new DefaultHeapVisitor() {           @Override           public boolean doObj(Oop oop) {               if (oldGen.isIn(oop.getHandle())) {                   oop.printValue();                   System.out.println();               }               return false;           }       }, klass);   }   public static void main(String[] args) {       new OldGen().execute(args);   } } 

:




.

— 7-8 JPoint 2017 . « JVM- », , , . «» , !

, JPoint Java — , .

Source: https://habr.com/ru/post/J325064/


All Articles