私たちは、10年以上の間に開発されたビジネスアプリケーション(100万以上のLOC)に取り組んでいます。 JDK8に切り替えると、JDK8のメタスペースに問題が発生します。これは、com.Sun.xml.ws:webservices-rt:1.4(Metro 1.4)で参照されるJaxB-Versionに関連しているようです。アプリケーションでの強力なリンクと、JaxBを介したクラス/インスタンスのレガシー作成のため、古いライブラリをオンザフライで切り替えるのは簡単ではありません。
現在、この問題を調査しています。この動作を再現するサンプルプログラムを作成しました。
import Java.io.ByteArrayInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class X
{
private static final String XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><x test=\"test\" />";
@XmlAttribute
String test;
public static void main( String[] args ) throws JAXBException, InterruptedException
{
System.out.println("start");
while ( true )
{
JAXBContext jc = JAXBContext.newInstance( X.class );
Unmarshaller unmarshaller = jc.createUnmarshaller();
X object = (X) unmarshaller.unmarshal( new ByteArrayInputStream( XML.getBytes() ) );
System.out.println( object.test );
}
}
}
JDK7は、PermGenSpaceをクリーンに保ちます。 (16M PermGenでシミュレート) JDK7での実行のメモリ
JDK8を使用すると、アプリケーションの実行速度が遅くなり、OOM例外が発生します。 VisualVMは例外をキャッチし、利用可能な最大のメタスペースでプロセスを実行し続けます。ここでもかなりの時間でmaxで動けなくなっています。 (16Mメタスペースでシミュレート) JDK8での実行のメモリ
ガベージコレクターのレガシー動作を取得する方法を誰かが知っているので、メモリ不足の問題に遭遇しませんか?それとも、この問題に対処する方法を他に考えていますか?
ありがとう。
edit1:実行パラメータJDK7:
-XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:MaxPermSize=16M -XX:PermSize=1M -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError
=>ヒープダンプは作成されません
実行パラメータJDK8:
-XX:+TraceClassLoading -XX:+TraceClassUnloading -XX:MaxMetaspaceSize=16M -XX:MetaspaceSize=1M -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError
=>実行中にヒープダンプが生成されます。
VisualVMの利用可能なメモリには、実際の最大メタスペース値は表示されません。制限がない場合、メタスペースはメモリを超えるまで常に増加します。
編集2:
JDK8で利用可能なすべてのガベージコレクターを試しました。彼らはすべて同じ問題を抱えています。
編集3:
実際のアプリケーションでは、JAXBとアプリケーションのいくつかのモジュール間の結合が激しいため、ライブラリを交換して解決することは困難です。したがって、短期的にはガベージコレクタの動作を修正する必要があります。長期的には、適切な修正はすでに計画されています。
次のVMパラメータを使用して、アプリケーションのすべての発生を修正できるまで、現在の問題を解決しました。
-Dcom.Sun.xml.bind.v2.bytecode.ClassTailor.noOptimize=true
これが同様の問題を持つ他の人を助けることを願っています...
クラスを非整列化するためのコンテキストを作成するには、JAXBContext.newInstance()を一度使用する必要があります。それ以外の場合は、permgenまたはメタスペースを使い果たします。
これがGaryが話しているソリューションです。これはフラグを設定するだけではありません( JAXBの人 でもシングルトンにすることを提案しているため...)
private static Map<class<?>, JAXBContext> contextStore = new ConcurrentHashMap<class<?>, JAXBContext>();
...
protected static JAXBContext getContextInstance(Class<?> objectClass) throws JAXBException{
JAXBContext context = contextStore.get(objectClass);
if (context==null){
context = JAXBContext.newInstance(objectClass);
contextStore.put(objectClass, context);
}
return context;
}
//using it like this:
JAXBContext context = getContextInstance(objectClass);