最近私は私の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]をクリックしてからサービスを再開します。
エラーが発生した場合、指定されたサービスはインストールされたサービスとして存在しません。
Tomcat6w //ES//servicename
ここで、servicenameは、services.mscで表示されるサーバーの名前です。
出典: Eric's Agile Answers に対するorxのコメント。
-XX:MaxPermSize=128M
よりも-XX:MaxPermGen=128M
を試してください。
このメモリプールの正確な使用方法はわかりませんが、JVMにロードされたクラスの数と関係があります。実行時にアプリケーションがクラスを生成してコンパイルする場合は、デフォルトより大きなメモリプールが必要になる可能性が高くなります。
よくある間違いは、ヒープスペースとパーマジェンスペースは同じだと考えていることですが、それはまったく真実ではありません。ヒープにはまだたくさんのスペースが残っているかもしれませんが、それでもpermgenのメモリが不足する可能性があります。
PermGenのOutofMemoryの一般的な原因はClassLoaderです。クラスがJVMにロードされるたびに、そのすべてのメタデータは、ClassloaderとともにPermGen領域に保持され、それらをロードしたClassloaderがガベージコレクションの準備ができたときにガベージコレクションされます。場合によっては、クラスローダは、それがロードしたすべてのクラスがメモリ内に残り、2、3回繰り返すとpermGenがメモリ不足になるよりもメモリリークが発生します。古典的な例は Java.lang.OutOfMemoryError:TomcatのPermGen Spaceです 。
今これを解決する2つの方法があります:
1。メモリリークの原因を特定するか、メモリリークがあるかどうかを調べます。
2。 JVMパラメーター-XX:MaxPermSize
および-XX:PermSize
を使用して、PermGen Spaceのサイズを大きくしてください。
詳細については、Javaで 2 Java.lang.OutOfMemoryError の解決策も確認できます。
Sun JVMの場合はコマンドラインパラメータ-XX:MaxPermSize=128m
を使用します(必要なサイズを128に置き換えます)。
-XX:MaxPermSize=256m
を試してみて、それが解決しない場合は-XX:MaxPermSize=512m
を試してください
私はEclipse ideを使っているので、I 追加-XX: MaxPermSize = 128m
(あなたはどれが一番うまくいくかを実験することができます)にVM Argumentsを入れます。ほとんどのJVMでは、 デフォルトのPermSize は64MB前後で、プロジェクトに含まれるクラスが多すぎたり、文字列の数が多すぎるとメモリ不足になります。
Eclipseの場合は、 answer にも記載されています。
ステップ1:Tomcatサーバーのサーバータブをダブルクリックします。
STEP 2:Confを起動し、既存のVM arguementsの最後に-XX: MaxPermSize = 128m
を追加します。
私は、複雑なWebアプリケーションのデプロイとアンデプロイの間にも、この問題に頭を悩ませてきました。そして、説明と解決策を追加したいと思いました。
アプリケーションをApache Tomcatにデプロイすると、そのアプリケーション用に新しいClassLoaderが作成されます。 ClassLoaderはその後、アプリケーションのすべてのクラスをロードするために使用され、アンデプロイ時にはすべてがうまくいってしまうはずです。しかし、実際にはそれほど単純ではありません。
Webアプリケーションの有効期間中に作成された1つ以上のクラスが静的参照を保持しています。この静的参照は、どこかでClassLoaderを参照しています。参照は元々静的であるため、ガベージコレクションによってこの参照がクリーンアップされることはありません。ClassLoader、およびそれがロードするすべてのクラスはそのまま残ります。
そして、2、3回の再デプロイの後、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();
最初にできることは、永久世代ヒープスペースのサイズを大きくすることです。通常の-Xms(初期ヒープサイズの設定)および-Xmx(最大ヒープサイズの設定)のJVM引数では、これを行うことはできません。この通常のJavaヒープスペースのためのスペースただし、(少なくともSun/OpenJDK jvmsでは)永続世代ヒープのサイズを大きくするために使用できる類似の引数があります。
-XX:MaxPermSize=128m
デフォルトは64メートルです。
それを解決するもう1つの方法は、PermGenが使い果たされないようにクラスをアンロードできるようにすることです。
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
そのようなものは過去に私のために魔法を働いた。ただし、permgenスイープでは、1回のリクエストごとに2回の追加リクエストが必要になるため、パフォーマンスが大幅に低下することがあります。あなたはあなたの使用とトレードオフのバランスをとる必要があるでしょう。
このエラーの詳細を見つけることができます。
http://faisalbhagat.blogspot.com/2014/09/Java-outofmemoryerror-permgen.html
Java.lang.OutOfMemoryError: PermGen
spaceメッセージは、メモリ内の常設世代の領域が使い果たされたことを示します。
どのJavaアプリケーションでも、限られた量のメモリを使用することが許可されています。特定のアプリケーションが使用できる正確なメモリ容量は、アプリケーションの起動時に指定されます。
Javaメモリは、次の図に示すようにさまざまな領域に分割されています。
メタスペース:新しいメモリスペースが生まれました
JDK 8 HotSpot JVMは現在、クラスメタデータの表現にネイティブメモリを使用しており、Metaspaceと呼ばれています。 Oracle JRockitおよびIBM JVMと同様です。
良い知らせは、Java.lang.OutOfMemoryError: PermGen
スペースの問題がなくなり、 Java_8_Download を使ってこのメモリスペースを調整して監視する必要がなくなることです。 -) 以上。
あるいは、Sunのjvmとは異なる方法でpermgenを処理するJRockitに切り替えることもできます。それは一般的に同様に優れたパフォーマンスを持っています。
http://www.Oracle.com/technetwork/middleware/jrockit/overview/index.html
最近の最も簡単な答えは、Java 8を使うことです。
PermGenメモリをPermGenスペース専用に予約しなくなり、PermGenメモリを通常のメモリプールと混在させることができます。
Java 8に何もしないと文句を言わせたくない場合は、標準ではない-XXPermGen...=...
JVM起動パラメータをすべて削除する必要があることに注意してください。
私たちがここで話している問題を抱えていました、私のシナリオはEclipse-helios + Tomcat + jsfであり、あなたがしていたことはTomcatに単純なアプリケーションをデプロイすることです。私はここで同じ問題を示していました、それを以下のように解決しました。
Eclipseでserversタブに移動します。私の場合はTomcat 7.0では登録されたサーバーをダブルクリックし、それは私のファイルサーバー一般登録情報を開きます。セクション「一般情報」をクリックしてください「起動設定を開く」、これは最後の2つのエントリに追加されたVM引数内のArgumentsタブでサーバーオプションの実行を開きます。
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
そして準備ができています。
[Javaオプション]テキスト領域に次の行を追加します。
-XX:MaxPermSize=128m
Perm genスペースエラーは、jvmがコードを実行するためにスペースを提供するのではなく、大きなスペースを使用することによって発生します。 UNIXオペレーティングシステムにおけるこの問題の最善の解決策は、bashファイルの設定を変更することです。次の手順で問題を解決してください。
端末でコマンドgedit .bashrc
を実行します。
以下の値でJava_OTPS
変数を作成します。
export Java_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
Bashファイルを保存してください。端末でexec bashコマンドを実行します。サーバーを再起動してください。
このアプローチがあなたの問題に役立つことを願っています。 8より前のバージョンのJavaを使用している場合、この問題が発生することがあります。しかし、Java 8を使用している場合は、この問題は起こりません。
パーマネントジェネレーションのサイズを大きくしたり、GCパラメータを微調整したりしても、本当のメモリリークがある場合は役に立ちません。あなたのアプリケーションやそれが使用するサードパーティのライブラリがリークしている場合は、クラスローダにリークを見つけてそれを修正するしかありません。あなたを助けることができる多くのツールがあります、最近の1つは Plumbr です。
Webアプリケーションでlog4jを使用している場合も、log4j のドキュメント でこの段落を確認してください。
PropertyConfigurator.configureAndWatch("log4j.properties")
を使用している場合、Webアプリケーションをアンデプロイするとメモリリークが発生するようです。
jrockit も私のためにこれを解決しました。しかし、サーブレットの再起動時間がはるかに悪いことに気付きました。そのため、本番環境では優れていましたが、開発ではちょっとした問題になりました。
私はHibernate + Eclipse RCPの組み合わせを持っていて、-XX:MaxPermSize=512m
と-XX:PermSize=512m
を使ってみました、そしてそれは私のために働いているようです。
私はいくつかの答えを試してみましたが、最後に仕事をしたのは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>
これが役立つことを願っています。
-XX:PermSize=64m -XX:MaxPermSize=128m
を設定してください。後でMaxPermSize
を増やすこともできます。うまくいくことを願っています。同じことが私にも言えます。 MaxPermSize
だけを設定してもうまくいきませんでした。
Tomcatにより多くのメモリを割り当てることは適切な解決策ではありません。
正しい解決策は、コンテキストが破棄されて再作成された後にクリーンアップを実行することです(ホットデプロイ)。解決策はメモリリークを止めることです。
Tomcat/Webアプリケーションサーバーからドライバの登録解除に失敗したことが通知された場合(JDBC)、ドライバの登録を解除します。これにより、メモリリークが防止されます。
ServletContextListenerを作成してweb.xmlで設定できます。これが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();
}
}
}
そして、ここであなたのweb.xmlでそれを設定します。
<listener>
<listener-class>
com.calculistik.mediweb.context.AppContextListener
</listener-class>
</listener>
彼らは、Tomcatの最新版(6.0.28または6.0.29)がサーブレットの再デプロイ作業をはるかにうまく処理すると言っています。
そのような場合の最初のステップは、GCがPermGenからクラスをアンロードできるかどうかを確認することです。標準的なJVMはこの点でかなり保守的です - クラスは永遠に生きるために生まれます。そのため、いったんロードされると、クラスはもはやそれらを使用していなくてもメモリ内に留まります。アプリケーションが大量のクラスを動的に作成し、生成されたクラスが長期間必要とされない場合、これは問題になる可能性があります。そのような場合は、JVMでクラス定義をアンロードできるようにすると便利です。これは、起動スクリプトに1つだけ設定パラメータを追加することで実現できます。
-XX:+CMSClassUnloadingEnabled
デフォルトではfalseに設定されているので、これを有効にするには、Javaオプションで次のオプションを明示的に設定する必要があります。 CMSClassUnloadingEnabledを有効にすると、GCはPermGenもスイープし、使用されなくなったクラスを削除します。このオプションは、UseConcMarkSweepGCも下記のオプションを使用して有効にした場合にのみ機能することに注意してください。そのため、ParallelGCを実行している場合、またはシリアルGCを禁止している場合は、次のように指定してGCをCMSに設定してください。
-XX:+UseConcMarkSweepGC
私はまったく同じ問題に遭遇しました、しかし残念ながら提案された解決策のどれも本当に私のために働きませんでした。問題は展開中には発生しませんでした。また、ホット展開もしていませんでした。
私の場合、問題は私のWebアプリケーションの実行中、データベースに接続している間(hibernate経由で)同じ時点で毎回起こりました。
このリンク (これも前述)は、問題を解決するのに十分な情報を提供しました。 jdbc-(mysql)ドライバをWEB-INFからjre/lib/ext /フォルダに移動すると、問題が解決したようです。新しいJREにアップグレードするにはドライバを再インストールする必要があるため、これは理想的な解決策ではありません。同様の問題を引き起こす可能性のあるもう1つの候補はlog4jです。そのため、これも移動させることをお勧めします。
メモリの設定はアプリの性質によって異なります。
何してるの?
処理されたトランザクションの量はいくらですか?
どのくらいのデータをロードしていますか?
等.
等.
等
おそらく、あなたはあなたのアプリをプロファイルし、あなたのアプリからいくつかのモジュールをクリーンアップし始めることができます。
どうやらこれはアプリケーションを数回再デプロイした後に起こります
Tomcatにはホットデプロイがありますが、メモリを消費します。たまにコンテナを再起動してみてください。また、プロダクションモードで実行するのに必要なメモリの量を知る必要があるでしょう、これはその研究にとって良い時間のようです。
--launcher.XXMaxPermSize
、-XX:MaxPermSize
などのパラメータを設定した後でも、Eclipse IDEでこれを取得している場合、それでも同じエラーが表示される場合は、EclipseがインストールされていたバグのあるバージョンのJREを使用している可能性があります。一部のサードパーティ製アプリケーションによってデフォルトに設定されています。これらのバグのあるバージョンはPermSizeパラメータを拾わないので、あなたが何を設定しても、あなたはまだこれらのメモリエラーを受け続けます。そのため、Eclipse.iniに以下のパラメーターを追加します。
-vm <path to the right JRE directory>/<name of javaw executable>
また、Eclipseの設定でデフォルトのJREを正しいバージョンのJavaに設定してください。
私にとってうまくいった唯一の方法は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スペース
私は上記の便利なヒントやテクニックをすべて試しましたが、残念ながら私にはうまくいきませんでした。私のために働いたことは以下のステップバイステップで説明されています:=>
<artifactId>maven-surefire-plugin</artifactId>
を検索する<configuration>
要素を追加し、次に<argLine>
サブ要素を追加して、-Xmx512m -XX:MaxPermSize=256m
を渡す=><configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>
それが役立つことを願って、幸せなプログラミング:)
私は6.0.29を実行していて、すべてのオプションを設定した後でも同じ問題を抱えているため、「それら」は間違っています。ティムハウランドが上で言ったように、これらのオプションは避けられないことを延期するだけです。これらを使用すると、再デプロイするたびにではなく、エラーが発生する前に3回再デプロイできます。
次のようにしてもこの問題を解決できます。
rm -rf <Tomcat-dir>/work/* <Tomcat-dir>/temp/*
workおよびtempディレクトリをクリアすると、Tomcatはクリーンな起動を実行します。