最近、Web アプリケーションで次のエラーに遭遇しました。
java.lang.OutOfMemoryError: PermGen スペース
これは、Tomcat 6 および JDK 1.6 で実行される典型的な Hibernate/JPA + IceFaces/JSF アプリケーションです。どうやらこれは、アプリケーションを数回再デプロイした後に発生する可能性があります。
何が原因で、それを避けるために何ができるでしょうか? 問題を解決するにはどうすればよいですか?
最近、Web アプリケーションで次のエラーに遭遇しました。
java.lang.OutOfMemoryError: PermGen スペース
これは、Tomcat 6 および JDK 1.6 で実行される典型的な Hibernate/JPA + IceFaces/JSF アプリケーションです。どうやらこれは、アプリケーションを数回再デプロイした後に発生する可能性があります。
何が原因で、それを避けるために何ができるでしょうか? 問題を解決するにはどうすればよいですか?
解決策は、Tomcat の起動時にこれらのフラグを JVM コマンド ラインに追加することでした。
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
これを行うには、Tomcat サービスをシャットダウンしてから、Tomcat/bin ディレクトリに移動し、tomcat6w.exe を実行します。[Java] タブで、引数を [Java オプション] ボックスに追加します。[OK] をクリックして、サービスを再起動します。
The specified service does not exist as an installed serviceというエラーが表示された場合は、次を実行する必要があります。
tomcat6w //ES//servicename
ここで、servicenameは、services.msc で表示されるサーバーの名前です。
出典: Eric's Agile Answersに関する orx のコメント。
-XX:MaxPermSize=128M
ではなく試してみてください-XX:MaxPermGen=128M
。
このメモリ プールの正確な使用方法はわかりませんが、JVM にロードされたクラスの数に関係しています。(したがって、Tomcat のクラス アンロードを有効にすると、問題を解決できます。) アプリケーションが実行時にクラスを生成およびコンパイルする場合、デフォルトよりも大きなメモリ プールが必要になる可能性が高くなります。
複数のデプロイ後に発生するアプリサーバーのPermGenエラーは、コンテナが古いアプリのクラスローダーに保持している参照が原因である可能性があります。たとえば、カスタムログレベルクラスを使用すると、参照がアプリサーバーのクラスローダーによって保持されます。これらのクラスローダー間リークは、jmapやjhatなどの最新の(JDK6 +)JVM分析ツールを使用して、アプリで引き続き保持されているクラスを確認し、それらの使用を再設計または排除することで検出できます。通常の容疑者は、データベース、ロガー、およびその他の基本フレームワークレベルのライブラリです。
クラスローダーのリーク:恐ろしい「java.lang.OutOfMemoryError:PermGenspace」例外、および特にそのフォローアップ投稿を参照してください。
人々が犯すよくある間違いは、ヒープスペースとpermgenスペースが同じであると考えることですが、これはまったく真実ではありません。ヒープに多くのスペースが残っている可能性がありますが、それでもpermgenのメモリが不足する可能性があります。
PermGenのOutofMemoryの一般的な原因は、ClassLoaderです。クラスがJVMにロードされるときはいつでも、クラスローダーとともにすべてのメタデータがPermGen領域に保持され、それらをロードしたクラスローダーがガベージコレクションの準備ができたときにそれらはガベージコレクションされます。場合によっては、クラスローダーにロードされたすべてのクラスがメモリに残り、2、3回繰り返すと、permGenのメモリ不足が発生するよりもメモリリークが発生します。古典的な例は、TomcatのJava.lang.OutOfMemoryError:PermGenSpaceです。
これを解決するには、次の2つの方法があります
。1。メモリリークの原因を特定するか、メモリリークがあるかどうかを確認します。2. JVMパラメータと
を使用して、PermGenスペースのサイズを増やします。-XX:MaxPermSize
-XX:PermSize
詳細については、JavaでのJava.lang.OutOfMemoryErrorの2ソリューションを確認することもできます。
-XX:MaxPermSize=128m
Sun JVMのコマンド ライン パラメータを使用します(明らかに、必要なサイズに 128 を代入します)。
試してみ-XX:MaxPermSize=256m
て、それが続く場合は試してください-XX:MaxPermSize=512m
Eclipse ide を使用しているため、 VM Argumentsに追加 -XX: MaxPermSize = 128m
しました(どちらが最適かを実験できます) 。ほとんどの JVM では、デフォルトの PermSizeは約64MBであり、プロジェクトにクラスが多すぎるか膨大な数の文字列がある場合、メモリが不足します。
Eclipse については、 answerにも記載されています。
ステップ 1 : [サーバー]タブで tomcat サーバーをダブルクリックします。
ステップ 2 : launch Conf-XX: MaxPermSize = 128m
を開き、既存のVM 引数の最後に追加します。
私も複雑な Web アプリケーションをデプロイおよびアンデプロイしているときに、この問題に頭を悩ませていたので、説明と解決策を追加したいと思いました。
アプリケーションを Apache Tomcat にデプロイすると、そのアプリケーション用に新しい ClassLoader が作成されます。次に、ClassLoader を使用してアプリケーションのすべてのクラスをロードし、アンデプロイすると、すべてがうまく消えるはずです。しかし、実際にはそれほど単純ではありません。
Web アプリケーションの存続期間中に作成された 1 つまたは複数のクラスは、どこかで ClassLoader を参照する静的参照を保持します。リファレンスはもともと静的であるため、いくらガベージ コレクションを行ってもこのリファレンスがクリーンアップされることはありません。ClassLoader と、ロードされたすべてのクラスはここに残ります。
そして、数回再デプロイした後、OutOfMemoryError が発生します。
現在、これはかなり深刻な問題になっています。再デプロイするたびに Tomcat を再起動することはできますが、再デプロイされるアプリケーションだけでなく、サーバー全体がダウンするため、多くの場合、実行できません。
その代わりに、Apache Tomcat 6.0 で動作するソリューションをコードにまとめました。他のアプリケーション サーバーでテストしたことはありません。他のアプリケーション サーバーでは変更しないと動作しない可能性が非常に高いことを強調しておく必要があります。
また、個人的にはこのコードが嫌いであり、適切なシャットダウンとクリーンアップの方法を使用するように既存のコードを変更できる場合、誰もこれを「迅速な修正」として使用すべきではないと言いたいです。これを使用する必要があるのは、コードが依存している外部ライブラリ (私の場合は RADIUS クライアント) があり、それ自体の静的参照をクリーンアップする手段を提供しない場合のみです。
とにかく、コードを続けます。これは、サーブレットの destroy メソッドまたは (より適切な方法) ServletContextListener の contextDestroyed メソッドなど、アプリケーションがアンデプロイされる時点で呼び出す必要があります。
//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
try {
classLoaderClassesField = clazz.getDeclaredField("classes");
} catch (Exception exception) {
//do nothing
}
clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);
List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));
for (Object o : classes) {
Class c = (Class)o;
//Make sure you identify only the packages that are holding references to the classloader.
//Allowing this code to clear all static references will result in all sorts
//of horrible things (like java segfaulting).
if (c.getName().startsWith("com.whatever")) {
//Kill any static references within all these classes.
for (Field f : c.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())
&& !Modifier.isFinal(f.getModifiers())
&& !f.getType().isPrimitive()) {
try {
f.setAccessible(true);
f.set(null, null);
} catch (Exception exception) {
//Log the exception
}
}
}
}
}
classes.clear();
java.lang.OutOfMemoryError: PermGen
スペース メッセージは、メモリ内の Permanent Generation の領域が使い果たされていることを示します。
すべての Java アプリケーションは、限られた量のメモリを使用できます。特定のアプリケーションが使用できるメモリの正確な量は、アプリケーションの起動時に指定されます。
Java メモリは、次の画像で確認できるさまざまな領域に分割されています。
Metaspace: 新たな記憶空間の誕生
JDK 8 HotSpot JVM は、クラス メタデータの表現にネイティブ メモリを使用するようになり、Metaspace と呼ばれます。Oracle JRockit および IBM JVM に似ています。
幸いなことjava.lang.OutOfMemoryError: PermGen
に、スペースの問題がなくなり、Java_8_Download以降を使用してこのメモリ スペースを調整および監視する必要がなくなりました。
最初にできることは、永続世代のヒープ領域のサイズを大きくすることです。これは、通常の –Xms (初期ヒープ サイズの設定) および –Xmx (最大ヒープ サイズの設定) JVM 引数では実行できません。前述のように、永続的な世代のヒープ領域は通常の Java ヒープ領域とは完全に分離されており、これらの引数が設定されるためです。この通常の Java ヒープ スペースのスペース。ただし、永久世代ヒープのサイズを大きくするために (少なくとも Sun/OpenJDK jvm で) 使用できる同様の引数があります。
-XX:MaxPermSize=128m
デフォルトは 64m です。
それを完全に処理する別の方法は、PermGen が不足しないようにクラスをアンロードできるようにすることです。
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
そのようなものは、過去に私にとって魔法のように機能しました。ただし、permgen スイープは、作成するリクエストごとに 2 つのリクエストを追加するか、これらの線に沿って何かを行うため、これらを使用するとパフォーマンスが大幅に低下します。使用とトレードオフのバランスを取る必要があります。
このエラーの詳細を確認できます。
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html
または、permgen を sun の jvm とは異なる方法で処理する JRockit に切り替えることもできます。一般的に、パフォーマンスも優れています。
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
ここで話している問題がありました。私のシナリオはEclipse-helios+tomcat + jsfであり、あなたが行っていたのは、tomcatへの単純なアプリケーションのデプロイです。私はここで同じ問題を示していましたが、次のように解決しました。
Eclipseで[サーバー]タブに移動し、私の場合はtomcat 7.0の登録済みサーバーをダブルクリックすると、ファイルサーバーの一般的な登録情報が開きます。[一般情報]セクションで[起動構成を開く]リンクをクリックすると、これら2つのエントリの最後に追加されたVM引数の[引数]タブでサーバーオプションの実行が開きます。
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
そして準備ができています。
[Java オプション] テキスト領域に次の行を追加します。
-XX:MaxPermSize=128m
jvm がコードを実行するために提供したスペースではなく、大きなスペースを使用したために、perm gen space エラーが発生します。
UNIX オペレーティング システムでのこの問題の最善の解決策は、bash ファイルの構成を変更することです。次の手順で問題を解決します。
gedit .bashrc
ターミナルでコマンドを実行します。
JAVA_OTPS
次の値で変数を作成します。
export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
bash ファイルを保存します。ターミナルでコマンド exec bash を実行します。サーバーを再起動します。
このアプローチがあなたの問題に役立つことを願っています。8 未満の Java バージョンを使用すると、この問題が発生することがあります。しかし、Java 8 を使用している場合、問題は発生しません。
また、webapp で log4j を使用している場合は、log4jドキュメントのこの段落を確認してください。
を使用している場合PropertyConfigurator.configureAndWatch("log4j.properties")
、webapp のアンデプロイ時にメモリ リークが発生するようです。
Permanent Generation のサイズを大きくしたり、GC パラメータを微調整したりしても、実際にメモリ リークが発生している場合は役に立ちません。アプリケーションまたはそれが使用するサードパーティ ライブラリがクラス ローダーをリークしている場合、唯一の現実的かつ恒久的な解決策は、このリークを見つけて修正することです。役立つツールはたくさんありますが、最近のツールの 1 つは、必要な機能を備えた新しいバージョンをリリースしたばかりのPlumbrです。
jrockitもこれを解決しました。ただし、サーブレットの再起動時間ははるかに悪いことに気付きました。そのため、本番環境では改善されていましたが、開発では一種の障害でした.
私はHibernate + Eclipse RCPを組み合わせて使用してみ-XX:MaxPermSize=512m
ましたが、うまく-XX:PermSize=512m
いっているようです。
設定し-XX:PermSize=64m -XX:MaxPermSize=128m
ます。後で を増やしてみることもできますMaxPermSize
。うまくいくことを願っています。同じことが私にとってもうまくいきます。設定だけでMaxPermSize
はうまくいきませんでした。
私はいくつかの答えを試しましたが、最終的に仕事をしたのは、pomのコンパイラプラグインのこの構成だけでした:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<fork>true</fork>
<meminitial>128m</meminitial>
<maxmem>512m</maxmem>
<source>1.6</source>
<target>1.6</target>
<!-- prevent PermGen space out of memory exception -->
<!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
</configuration>
</plugin>
これが役立つことを願っています。
Assigning Tomcat more memory is NOT the proper solution.
The correct solution is to do a cleanup after the context is destroyed and recreated (the hot deploy). The solution is to stop the memory leaks.
If your Tomcat/Webapp Server is telling you that failed to unregister drivers (JDBC), then unregister them. This will stop the memory leaks.
You can create a ServletContextListener and configure it in your web.xml. Here is a sample ServletContextListener:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Logger;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
/**
*
* @author alejandro.tkachuk / calculistik.com
*
*/
public class AppContextListener implements ServletContextListener {
private static final Logger logger = Logger.getLogger(AppContextListener.class);
@Override
public void contextInitialized(ServletContextEvent arg0) {
logger.info("AppContextListener started");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
logger.info("AppContextListener destroyed");
// manually unregister the JDBC drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
logger.info(String.format("Unregistering jdbc driver: %s", driver));
} catch (SQLException e) {
logger.info(String.format("Error unregistering driver %s", driver), e);
}
}
// manually shutdown clean up threads
try {
AbandonedConnectionCleanupThread.shutdown();
logger.info("Shutting down AbandonedConnectionCleanupThread");
} catch (InterruptedException e) {
logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
e.printStackTrace();
}
}
}
And here you configure it in your web.xml:
<listener>
<listener-class>
com.calculistik.mediweb.context.AppContextListener
</listener-class>
</listener>
Tomcat の最新リビジョン (6.0.28 または 6.0.29) は、サーブレットの再デプロイのタスクをより適切に処理すると彼らは言います。
メモリの構成は、アプリの性質によって異なります。
何してるの?
処理されたトランザクションの量は?
どのくらいのデータをロードしていますか?
等
等
等
おそらく、アプリのプロファイルを作成して、アプリからいくつかのモジュールのクリーンアップを開始できます。
どうやらこれは、アプリケーションを数回再デプロイした後に発生する可能性があります
Tomcat にはホット デプロイがありますが、メモリを消費します。時々コンテナを再起動してみてください。また、本番モードで実行するために必要なメモリの量を知る必要があります。これは、その調査に適した時期と思われます。
まったく同じ問題に遭遇しましたが、残念ながら、提案された解決策はどれも実際にはうまくいきませんでした。展開中に問題は発生せず、ホット展開も行っていませんでした。
私の場合、(休止状態を介して) データベースに接続しているときに、Web アプリケーションの実行中に毎回同じ時点で問題が発生しました。
このリンク(これも前述) は、問題を解決するのに十分な内部情報を提供しました。jdbc-(mysql)-driver を WEB-INF から jre/lib/ext/ フォルダーに移動すると、問題が解決したようです。新しい JRE にアップグレードするにはドライバーを再インストールする必要があるため、これは理想的なソリューションではありません。同様の問題を引き起こす可能性のある別の候補は log4j であるため、これも移動することをお勧めします。
このような場合の最初のステップは、GC が PermGen からクラスをアンロードできるかどうかを確認することです。標準の JVM は、この点に関してかなり保守的です。つまり、クラスは永遠に存続するために生まれてきます。そのため、いったん読み込まれると、コードがクラスを使用しなくなったとしても、クラスはメモリ内に残ります。これは、アプリケーションが動的に多数のクラスを作成し、生成されたクラスが長期間必要とされない場合に問題になる可能性があります。このような場合、JVM がクラス定義をアンロードできるようにすると便利です。これは、起動スクリプトに 1 つの構成パラメーターを追加するだけで実現できます。
-XX:+CMSClassUnloadingEnabled
デフォルトではこれは false に設定されているため、これを有効にするには、Java オプションで次のオプションを明示的に設定する必要があります。CMSClassUnloadingEnabled を有効にすると、GC は PermGen もスイープし、使用されなくなったクラスを削除します。このオプションは、以下のオプションを使用して UseConcMarkSweepGC も有効になっている場合にのみ機能することに注意してください。そのため、ParallelGC またはシリアル GC を実行する場合は、次のように指定して GC を CMS に設定してください。
-XX:+UseConcMarkSweepGC
私にとってうまくいった唯一の方法は、JRockit JVM を使用することでした。MyEclipse 8.6 を使用しています。
JVM のヒープには、実行中の Java プログラムによって生成されたすべてのオブジェクトが格納されます。Java はnew
演算子を使用してオブジェクトを作成し、新しいオブジェクトのメモリは実行時にヒープに割り当てられます。ガベージ コレクションは、プログラムによって参照されなくなったオブジェクトに含まれるメモリを自動的に解放するメカニズムです。
私は同様の問題を抱えていました。私のはJDK 7 + Maven 3.0.2 + Struts 2.0 + Google GUICE依存性注入ベースのプロジェクトです。
mvn clean package
コマンドを実行しようとすると、次のエラーが表示され、 「BUILD FAILURE」が発生しました
org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; ネストされた例外は java.lang.reflect.InvocationTargetException: null java.lang.reflect.InvocationTargetException 原因: java.lang.OutOfMemoryError: PermGen space
上記の役立つヒントとコツをすべて試しましたが、残念ながらどれもうまくいきませんでした。私にとってうまくいったことは、以下に段階的に説明されています:=>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
要素を追加してから、以下に示すよう<argLine>
にパスするサブ要素を追加します =>-Xmx512m -XX:MaxPermSize=256m
<configuration>
<argLine>-Xmx512m -XX:MaxPermSize=256m</argLine>
</configuration>
それが役立つことを願って、幸せなプログラミング:)
Eclipse IDE でこれを取得している場合、パラメータ
--launcher.XXMaxPermSize
、-XX:MaxPermSize
などを設定した後でも同じエラーが発生する場合は、Eclipse がバグのあるバージョンの JRE を使用している可能性があります。サード パーティのアプリケーションであり、デフォルトに設定されています。これらのバグのあるバージョンは PermSize パラメータを取得しないため、設定に関係なく、これらのメモリ エラーが発生し続けます。したがって、eclipse.ini に次のパラメーターを追加します。
-vm <path to the right JRE directory>/<name of javaw executable>
また、Eclipse の設定でデフォルトの JRE を正しいバージョンの Java に設定していることを確認してください。
私は6.0.29を実行していて、すべてのオプションを設定した後でも同じ問題があるため、「彼ら」は間違っています。上でティム・ハウランドが言ったように、これらのオプションは避けられないことを先延ばしにするだけです。再デプロイするたびにではなく、エラーが発生する前に 3 回再デプロイできます。
この問題は、次の方法でも解決できます。
rm -rf <tomcat-dir>/work/* <tomcat-dir>/temp/*
workおよびtempディレクトリをクリアすると、 Tomcat はクリーンな起動を行います。