JVMのパフォーマンスの問題を見つけると、メモリリークに遭遇し、JVM OutOfMemoryを引き起こす可能性があります。 parameter reloadable = "true"がTomcatコンテナを使用するときに設定されている場合、アプリケーションを頻繁に展開するときにメモリオーバーフローに遭遇する可能性もあります。 Tomcatのホット展開の原則は、Web-INF/クラスまたはWeb-INF/LIBディレクトリのファイルが変更され、アプリケーションが最初に停止してから開始されることを検出することです。 TomcatはデフォルトでWebAppClassLoaderを各アプリケーションに割り当てるため、ホット交換の原則は、クラスをロードする新しいクラスローダーを作成することです。 JVMのクラスの独自性はクラスファイルとクラスローダーによって決定されるため、クラスをリロードすると、ホット交換の目的を達成できます。ホット展開の数がより頻繁になると、JVMによってロードされるクラスが増えます。前のクラスが何らかの理由で時間内にアンロードされていない場合(メモリリークなど)、永続的な発電またはメタスカスアウトオフメモリにつながる可能性があります。この記事では、ThreadlocalとClassloaderがデモを介してメモリリークを導き、最終的にOutMemoryにつながるシナリオを簡単に紹介します。
クラスのアンインストール
クラスが使用された後、次の状況が満たされた場合、アンインストールされます。
1.ヒープ内のこのクラスのすべてのインスタンスはリサイクルされています。つまり、このクラスのインスタンスオブジェクトはヒープに存在しません。
2。このクラスのロードされたクラスローダーはリサイクルされています。
3.このクラスに対応するクラスオブジェクトはどこでも参照できず、クラスオブジェクトに反射を通じてアクセスできません。
クラスがアンインストール条件を満たしている場合、JVMはGCにあるときにクラスをアンインストールします。つまり、メソッド領域のクラス情報をクリアします。
シーンの紹介
前の記事では、threadlocalの原則を紹介しました。各スレッドにはthreadlocalMapがあります。スレッドのライフサイクルが比較的長い場合、ThreadLocalMapのエントリがリサイクルされない場合があります。 threadlocalオブジェクトは、常にスレッドによって強く参照されています。インスタンスオブジェクトはクラスオブジェクトの参照を保持するため、クラスオブジェクトはそれをロードするクラスローダーの参照を保持し、クラスがアンロードされます。十分なクラスがロードされている場合、永続的な生成またはメタスパースメモリオーバーフローが発生する可能性があります。クラスに大きなバイト配列などの大きなオブジェクトがある場合、Javaヒープ領域のメモリオーバーフローが発生します。
ソースコードの紹介
これが内部クラスの内側です。内側のクラスには静的なスレッドローカルオブジェクトがあり、主にスレッドが内側のクラスを強く参照するために使用されるため、内部クラスをリサイクルできません。以下に示すように、カスタムクラスローダーは、内部クラスをロードするために定義されています。
パブリッククラスメモリリーク{public static void main(string [] args){//スレッドは常に実行されているため、threadlocalmapの内側オブジェクトはnew runnable(){@override public void run(){while(true){// new classloderdoldolodeer customlodadloded custldoldoloded customlodoldoloded custldoldoloded custldoldoloded custllodoldoloded custllodedloadlodedのロードを作成するために作成される{while(){// (load1 "、MemoryLeak.getClassLoader()、" com.ezlippi.memoryLippi.memoryLeak $ Inner $ 1 ") innerclass = null = null; } //ヒープ領域に到達するために、より高速なpublic static classインナー{private byte [] mb = new byte [1024 * 1024]; static threadlocal <nenter> threadlocal = new threadlocal <nenter>(){@override protected innerityvalue(){return new inner(); }}; // threadlocal.get()を呼び出して、内側のオブジェクトを初期化stathic {threadlocal.get(); } public inner(){}} //ソースコードはプライベート静的クラスカスタムクラスローダーを省略しますclassloader {}ヒープエリアメモリオーバーフロー
ヒープメモリオーバーフローをトリガーするために、内部クラスに1MBバイト配列を設定し、同時に、静的ブロックでthreadlocal.get()を呼び出す必要があります。 CallのみがInitialValue()をトリガーして内側のオブジェクトを初期化します。そうしないと、空のThreadLocalオブジェクトを作成するだけで、ThreadLocalMapにデータはありません。
JVMパラメーターは次のとおりです。
-xms100m -xmx100m -xx:+useparnewgc -xx:+useconcmarksweepgc -xx:+printgcdetails -xx:+printheapatgc -xx:+printclasshistogram -xx:+heapumponoutofmemoreror
以下に示すように、最後の814件の実行の後、JVMヒープエリアメモリがオーバーフローします。
java.lang.outofmemoryerror:Java Heap spacedumping Heap to Java_pid11824.hprof ... Heap Dumpファイル作成[100661202バイト1.501秒] 27328K、99%は[0x000000F9C000000、0x000000000000FB6AD450、0X000000FB6B0000)を使用しました。スペース3392Kから、90%は[0x00000000000000FB6B0000、0X000000FB9B0030、0X00000000FBA00000)に使用されます。 [0x0000000000FBA00000、0x00000000FBD500000)同時マークスイープ生成合計68288K、67600K [0x000000000FBD500000、0x0000000000000000000000000000000000000000000000000000000000000000000、0x000000010000000)3770K、容量5134K、コミット556K、積立5248K 474K、容量578K、積分640K、640K、スレッド「スレッド0」Java.lang.outofMemoryError:com.ezlippi.mezlippi.memoryLeak $ inner。 sun.refllect.nativeconstructoraCsorimpl.newinstance0(ネイティブメソッド)sun.reflt.nativeconstructoraccessorimpl.newinstance(未知のソース)sun.reflect.delegatingconstructoraccessorimpl.newinstance(未知のソース)aTANG.Reng.Reng.Reng.Reff.Constructurturcunturturturcunturconce java.lang.lang.constructor.newinstance(不明なソース)ソース)java.lang.class.newinstance(com.ezlippi.memoryLeak $ 1.RUN(MEMORYLEAK.JAVA:20)のJava.lang.thread.run(未知のソース)のcom.ezlippi.memoryLun(MemoryLeak.java:20)
ヒープエリアには多くの1MBバイト配列が保存されているため、JVMには新しい内部オブジェクトを作成するメモリがないことがわかります。ここでは、クラスのヒストグラムを印刷しました(次の図は、ヒープサイズの1024mのシーンです)。わずかなクラスを省略しました。バイトアレイが855mのスペースを占有し、814インスタンスのcom.ezlippi.memoryLeak $ customClassLoaderが作成されていることがわかります。これは、基本的にバイト配列のサイズと一致します。
num #instances #bytesクラス 名前 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- com.ezlippi.memoryLeak $ customClassloader 12:820 53088 [ljava.util.hashtable $ entry; 15:817 39216 java.util.hashtable 16:915 36600 Java.lang.ref.softreference 17:543 34752 java.net.url 18:697 33456 java.nio.heapcharbuffer 19:817 32680 Java.security dudiurual java.util.treemap $ entry 21:928 29696 java.util.hashtable $ entry 22:1802 28832 java.util.hashset 23:817 26144 java.security.codesource 24:814 26048 Java.lang.threadlocal $ treadlmap $ Entry $ Entry
Metaspace Overflow
Metaspace Overflowを作成するには、Heap Overflowの前に、Metaspaceのスペースを少し減らし、十分なクラスをロードする必要があります。したがって、以下に示すように、JVMパラメーターを調整し、バイト配列のサイズを1KBに調整しました。
private byte [] kb = new byte [1024]; -xms100m -xmx100m -xx:+useparnewgc -xx:+useconcmarksweepgc -xx:+printgcdetails -xx:+printheapatgc -xx:+printhistogram -classogram -2mm -xxx:+printhistograms:+printh
GCログから、MeraspaceがGCしきい値に達すると(つまり、Maxmetaspacesizeの構成のサイズ)、FullGCがトリガーされることがわかります。
java.lang.outofmemoryerror:metaspace << Stackトレースなし>> {GC Invocations = 20(Full 20):PAR New Generation Total 30720K、使用0K [0x00000000FBD500000) 0x00000000F9C00000、0x0000000F9C00000、0x0000000F9C00000、0x0000000FB6B0000)スペース3392Kから、0%使用[0x0000000000FBA00000、0x000000000FBD500000)スペース3392K、0%使用[0x0000000000FBA00000、0x00000001000000000000)1806K、使用容量1988K、献身2048K、105678Kクラススペースを使用しました。 1048576K [フルGC(メタデータGCしきい値)[CMSProcess Exit Code 1で終了上記の例から、クラスローダーとThreadlocalが不適切に使用されている場合、実際にメモリの漏れにつながることがわかります。完全なソースコードはgithubにあります