質問

最近、Web アプリケーションで次のエラーが発生しました。

java.lang.OutOfMemoryエラー: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

どこ サービス名 services.msc に表示されるサーバーの名前です。

ソース:orxさんのコメント エリックのアジャイルな回答.

他のヒント

試してみたほうがいいよ -XX:MaxPermSize=128M それよりも -XX:MaxPermGen=128M.

このメモリ プールの正確な用途はわかりませんが、JVM にロードされるクラスの数に関係しています。(したがって、Tomcat のクラス アンロードを有効にすると、問題を解決できます。) アプリケーションが実行中にクラスを生成およびコンパイルする場合、デフォルトよりも大きなメモリ プールが必要になる可能性が高くなります。

複数のデプロイ後に発生するアプリサーバーの PermGen エラーは、コンテナーによって保持されている古いアプリのクラスローダーへの参照が原因で発生する可能性が高くなります。たとえば、カスタム ログ レベル クラスを使用すると、アプリ サーバーのクラスローダーによって参照が保持されます。jmap や jhat などの最新 (JDK6+) JVM 分析ツールを使用してアプリ内で保持され続けるクラスを確認し、それらの使用を再設計または削除することで、これらのクラスローダー間リークを検出できます。通常、疑わしいのはデータベース、ロガー、およびその他の基本フレームワーク レベルのライブラリです。

見る クラスローダーのリーク:恐ろしい「java.lang.OutOfMemoryError:PermGen スペース」例外, 、特にその フォローアップ投稿.

よくある間違いは、ヒープ領域と permgen 領域が同じであると考えることですが、これはまったく真実ではありません。ヒープに多くのスペースが残っている場合でも、permgen のメモリが不足する可能性があります。

PermGen での OutofMemory の一般的な原因は ClassLoader です。クラスが JVM にロードされるたびに、そのすべてのメタデータはクラスローダーとともに PermGen 領域に保持され、それらをロードしたクラスローダーがガベージ コレクションの準備ができたときに、それらのメタ データがガベージ コレクションされます。クラスローダーでメモリ リークが発生した場合、クラスローダによってロードされたすべてのクラスがメモリ内に残り、数回繰り返すと permGen のメモリ不足が発生します。古典的な例は次のとおりです Java.lang.OutOfMemoryError:Tomcat の PermGen スペース.

これを解決するには 2 つの方法があります。
1.メモリ リークの原因、またはメモリ リークがあるかどうかを調べます。
2.JVM パラメータを使用して PermGen Space のサイズを増やす -XX:MaxPermSize そして -XX:PermSize.

チェックすることもできます 2 Java.lang.OutOfMemoryError の解決策 詳細については、Java を参照してください。

コマンドラインパラメータを使用する -XX:MaxPermSize=128m Sun JVM の場合 (明らかに、必要なサイズを 128 に置き換えます)。

試す -XX:MaxPermSize=256m それが続く場合は試してください -XX:MaxPermSize=512m

追加した -XX: MaxPermSize = 128m (どれが最も効果的かを実験できます) VMの引数 私はEclipse IDEを使用しているので。ほとんどの JVM では、 デフォルトの PermSize 周りにいる 64MB プロジェクト内にクラスが多すぎるか、文字列が膨大な数にある場合、メモリが不足します。

Eclipse については、次の場所でも説明されています。 答え.

ステップ1 :Tomcat サーバーをダブルクリックします。 サーバー タブ

enter image description here

ステップ2 : 起動会議を開く そして追加します -XX: MaxPermSize = 128m 既存の終わりまで VM の引数.

enter image description here

私も複雑な 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();

あるいは、permgen を sun の jvm とは異なる方法で処理する JRockit に切り替えることもできます。一般的にパフォーマンスも優れています。

http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html

1) PermGen メモリ サイズの増加

最初にできることは、永続世代ヒープ領域のサイズを大きくすることです。これは、通常の–XMS(初期ヒープサイズの設定)と–XMX(最大ヒープサイズの設定)JVM引数では実行できません。前述のように、永続的な世代のヒープスペースは通常のJavaヒープスペースとは完全に分離されており、これらの引数は設定されています。この通常のJavaヒープスペースのスペース。ただし、(少なくとも Sun/OpenJDK jvms では) 永続生成ヒープのサイズを大きくするために使用できる同様の引数があります。

 -XX:MaxPermSize=128m

デフォルトは64mです。

2) スイープを有効にする

これを永久に処理するもう 1 つの方法は、PermGen が枯渇しないようにクラスをアンロードできるようにすることです。

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

そのようなことは、過去の私にとって魔法のように働きました。ただし、permgen スイープでは、リクエストごとに 2 つのリクエストが追加されるため、これらを使用するとパフォーマンスが大幅に低下します。使用方法とトレードオフのバランスを取る必要があります。

このエラーの詳細はこちらで確認できます。

http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html

java.lang.OutOfMemoryError: PermGen space メッセージは、メモリ内の永続世代の領域が使い果たされたことを示します。

どの Java アプリケーションでも、限られた量のメモリを使用できます。特定のアプリケーションが使用できる正確なメモリ量は、アプリケーションの起動時に指定されます。

Java メモリは、次の図に示すようにさまざまな領域に分割されています。

enter image description here

メタスペース:新しい記憶空間が生まれる

JDK 8 HotSpot JVM は、クラス メタデータの表現にネイティブ メモリを使用するようになり、メタスペースと呼ばれます。Oracle JRockit や IBM JVM に似ています。

良いニュースは、それはもう意味がないということです java.lang.OutOfMemoryError: PermGen スペースの問題が発生するため、このメモリスペースを調整して監視する必要はもうありません。 Java_8_ダウンロード 以上。

ここで話している問題がありました。私のシナリオはeclipse-helios + tomcat + jsfであり、あなたがやっていたのは、単純なアプリケーションをtomcatにデプロイすることです。ここで同じ問題を示していましたが、次のように解決しました。

日食では、次の場所に移動します サーバー 私の場合、Tomcat 7.0 の場合、タブで登録済みサーバーをダブルクリックすると、ファイル サーバーの一般登録情報が開きます。セクションについて "一般情報" リンクをクリックしてください 「起動設定を開く」 これにより、最後にこれら 2 つのエントリに追加された VM 引数の [引数] タブでサーバー オプションの実行が開きます。

-XX: MaxPermSize = 512m
-XX: PermSize = 512m

そして準備完了。

最近の最も簡単な答えは、Java 8 を使用することです。

PermGen スペース専用にメモリを予約しなくなり、PermGen メモリを通常のメモリ プールと混在させることができます。

標準以外のものはすべて削除する必要があることに注意してください -XXPermGen...=... Java 8 が何もしないというエラーを表示したくない場合は、JVM 起動パラメータを使用します。

  1. TomcatのBinディレクトリからTomcat7Wを開くか、スタートメニューでTomcatを監視します(さまざまなサービス情報でタブ付きウィンドウが開きます)。
  2. 「Java オプション」テキスト領域に次の行を追加します。

    -XX:MaxPermSize=128m
    
  3. 初期メモリ プールを 1024 に設定します (オプション)。
  4. 最大メモリ プールを 1024 に設定します (オプション)。
  5. 「OK」をクリックします。
  6. Tomcat サービスを再起動します。

Perm gen space エラーは、コードを実行するために jvm が提供するスペースではなく、大きなスペースを使用するために発生します。UNIX オペレーティング システムにおけるこの問題に対する最善の解決策は、bash ファイルの構成を変更することです。次の手順で問題を解決します。

コマンドの実行 gedit .bashrc 端末上で。

作成する JAVA_OTPS 次の値を持つ変数:

export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"

bash ファイルを保存します。ターミナルでコマンド exec bash を実行します。サーバーを再起動します。

このアプローチがあなたの問題に役立つことを願っています。Java バージョン 8 より前のバージョンを使用している場合、この問題が発生することがあります。ただし、Java 8 を使用する場合、問題は発生しません。

実際にメモリ リークが発生している場合は、永続世代のサイズを増やしたり、GC パラメータを調整したりしても役に立ちません。アプリケーションまたはアプリケーションが使用するサードパーティ ライブラリでクラス ローダーがリークしている場合、唯一の現実的かつ永続的な解決策は、このリークを見つけて修正することです。役立つツールは数多くありますが、最近のツールの 1 つは次のとおりです。 プランバー, 、必要な機能を備えた新しいバージョンがリリースされました。

また、Web アプリで log4j を使用している場合は、log4j のこの段落を確認してください。 ドキュメンテーション.

を使用している場合のようです PropertyConfigurator.configureAndWatch("log4j.properties"), 、Web アプリのデプロイを解除するとメモリ リークが発生します。

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>

これがお役に立てば幸いです。

私もこれを解決しました。ただし、サーブレットの再起動時間がかなり悪くなることに気付きました。そのため、実稼働環境では改善されても、開発では多少の足かせになっていました。

メモリの構成はアプリの性質によって異なります。

何してるの?

処理されたトランザクションの量はどれくらいですか?

どれくらいのデータをロードしていますか?

おそらく、アプリのプロファイリングを行って、アプリからいくつかのモジュールのクリーンアップを開始できるでしょう。

どうやら、アプリケーションを数回再デプロイした後にこの問題が発生する可能性があります

Tomcat にはホット デプロイがありますが、メモリを消費します。時々コンテナを再起動してみてください。また、実稼働モードで実行するために必要なメモリの量を知る必要もあります。これは、その調査には良い時期だと思われます。

Tomcat の最新バージョン (6.0.28 または 6.0.29) がサーブレットの再デプロイのタスクを処理すると言われています。 多くの より良い。

私もまったく同じ問題に遭遇しましたが、残念ながら、提案された解決策はどれも私にとってはうまくいきませんでした。この問題は展開中には発生しませんでした。また、ホット展開も行っていませんでした。

私の場合、(休止状態経由で) データベースに接続しているときに、Web アプリケーションの実行中の同じ時点で問題が毎回発生しました。

このリンク (前にも述べました) 問題を解決するのに十分な内部が提供されました。jdbc-(mysql)-driver を WEB-INF から jre/lib/ext/ フォルダーに移動すると、問題が解決したようです。新しい JRE にアップグレードするにはドライバーを再インストールする必要があるため、これは理想的な解決策ではありません。同様の問題を引き起こす可能性のあるもう 1 つの候補は log4j なので、これも移動するとよいでしょう。

このような場合の最初のステップは、GC が PermGen からクラスをアンロードできるかどうかを確認することです。標準の JVM はこの点ではかなり保守的です。クラスは永遠に存続するために生まれます。そのため、クラスがロードされると、それを使用するコードがなくなっても、クラスはメモリ内に残ります。これは、アプリケーションが多数のクラスを動的に作成し、生成されたクラスが長期間必要とされない場合に問題になる可能性があります。このような場合、JVM がクラス定義をアンロードできるようにすると便利です。これは、起動スクリプトに 1 つの構成パラメータを追加するだけで実現できます。

-XX:+CMSClassUnloadingEnabled

デフォルトでは、これは false に設定されているため、これを有効にするには、Java オプションで次のオプションを明示的に設定する必要があります。CMSClassUnloadingEnabled を有効にすると、GC は PermGen もスイープし、使用されなくなったクラスを削除します。このオプションは、以下のオプションを使用して UseConcMarkSoupGC も有効になっている場合にのみ機能することに注意してください。したがって、ParallelGC または、絶対にシリアル GC を実行するときは、次のように指定して GC を CMS に設定していることを確認してください。

-XX:+UseConcMarkSweepGC

Tomcat にさらに多くのメモリを割り当てることは、適切な解決策ではありません。

正しい解決策は、コンテキストが破棄されて再作成された後にクリーンアップを実行することです (ホット デプロイ)。解決策は、メモリ リークを停止することです。

Tomcat/Webapp サーバーがドライバー (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>  

私は6.0.29を実行していて、すべてのオプションを設定した後でも同じ問題が発生するため、「それら」は間違っています。ティム・ハウランドが上で述べたように、これらの選択肢は避けられない事態を先送りするだけです。毎回再デプロイするのではなく、エラーが発生する前に 3 回再デプロイすることができます。

パラメータを設定した後でも、Eclipse IDE でこれが発生する場合--launcher.XXMaxPermSize, -XX:MaxPermSize, など、それでも同じエラーが発生する場合は、サードパーティのアプリケーションによってインストールされ、デフォルトに設定されているバグのあるバージョンの JRE を Eclipse が使用している可能性が最も高くなります。これらのバグのあるバージョンでは 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 コマンドを実行すると、次のエラーが表示され、 「ビルド失敗」 発生した

org.apache.maven.surefire.util.SurefireReflectionException:java.lang.reflect.InvocationTargetException;ネストされた例外は java.lang.reflect.InvocationTargetException です。null java.lang.reflect.invocationTargetExceptionが原因:java.lang.OutOfMemoryエラー:PermGen スペース

上記の役立​​つヒントやテクニックをすべて試してみましたが、残念ながらどれもうまくいきませんでした。私にとってうまくいったことを以下にステップバイステップで説明します:=>

  1. pom.xml に移動します
  2. 検索する <artifactId>maven-surefire-plugin</artifactId>
  3. 新規追加 <configuration> 要素、そして <argLine> パスが含まれるサブ要素 -Xmx512m -XX:MaxPermSize=256m 以下に示すように =>

<configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>

お役に立てば幸いです、プログラミングを楽しんでください:)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top