0

mstorライブラリを使用してmboxメールファイルを解析しています。一部のファイルのサイズがギガバイトを超えています。ご想像のとおり、これによりヒープスペースの問題が発生する可能性があります。

反復ごとに特定のメッセージを取得するループがあります。呼び出しは、getMessage()ヒープスペースが不足したときに割り当てようとしているものです。このループの先頭にに呼び出しを追加するとSystem.gc()、プログラムは大きなファイルをエラーなしで解析しますが、ガベージを40,000回収集すると、プログラムの速度が低下する必要があることに気付きます。

私の最初の試みは、if (i % 500 == 0) System.gc()500レコードごとに呼び出しが発生するように呼び出すことでした。この数値を上げ下げしてみましたが、結果に一貫性がなく、通常はOutOfMemoryエラーが返されます。

私の2番目の、より巧妙な試みは次のようになります。

try {
    message = inbox.getMessage(i);
} catch (OutOfMemoryError e) {
    if (firstTry) {
        i--;
        firstTry = false;
    } else {
        firstTry = true;
        System.out.println("Message " + i + " skipped.");
    }
    System.gc();
    continue;
}

OutOfMemoryエラーがスローされた場合にのみガベージコレクターを呼び出し、カウントをデクリメントして再試行するという考え方です。残念ながら、数千通の電子メールを解析した後、プログラムは出力を開始します。

 Message 7030 skipped.
 Message 7031 skipped.
 ....

残りの部分についても同様です。

反復ごとにコレクターをヒットすると、これとは異なる結果がどのように返されるかについて、私は混乱しています。私の理解では、ごみはごみであり、これを変える必要があるのは、特定の時間に収集される量だけです。

誰かがこの奇妙な行動を説明できますか?コレクターに電話をかける頻度を減らす他の方法についての推奨事項はありますか?ヒープスペースがいっぱいになりました。

4

5 に答える 5

1

呼び出しSystem.gc()は一般的な意味で時間の無駄であり、いつでも何かをすることを保証するものではありません。それはせいぜい提案であり、ほとんどの場合無視されます。例外がスローされる前にJVMがすでにメモリを再利用しようとしているため、の後に呼び出すことOutOfMemoryExceptionはさらに役に立ちません。

制御できないサードパーティのコードを使用している場合に実行できる唯一のことは、コマンドラインでのJVMヒープ割り当てを、特定のマシンが処理できる最大数まで増やすことです。

Java JVMメモリ(ヒープ、スタック、-xss -xms -xmx -xmn ...)の使用を開始します

于 2012-06-21T17:08:31.360 に答える
1

これが私の提案です:

  • ヒープスペースを増やします。これはおそらく最も簡単なことです。これは、で行うことができます-Xmx。パラメータ。
  • メッセージをロードするAPIが「ストリーミング」オプションを提供するかどうかを確認します。おそらく、メッセージ全体を一度にメモリにロードする必要はありません。

GCが呼び出されることが保証されていないため、呼び出しSystem.gc()ても何の役にも立ちません。事実上、それは悪いコードの確かな兆候です。コードが機能することに依存している場合はSystem.gc()、コードが壊れている可能性があります。この場合、パフォーマンスのためにそれに依存しているように見えます。これは、コードが確実に壊れていることを示しています。

JVMが要求を受け入れるかどうかは決してわかりません。また、JVMがガベージコレクションをどのように実行するかもわかりません。JVMは、要求を完全に無視することを決定する場合があります(つまり、それは保証ではありません)。それが想定されていることをするかどうかSystem.gc()は、かなり気難しいです。その動作は保証されていないため、まったく使用しないことをお勧めします。

最後に、オプションを使用してへの明示的な呼び出しを無効にできます。つまり、明示的な呼び出しを無視するように構成されたJVMで実行されている可能性があるため、呼び出しが実行される保証はありません。System.gc()-XX:DisableExplicitGCSystem.gc()

于 2012-06-21T17:56:40.783 に答える
1

デフォルトでは、mstor はより高速なアクセスのために、フォルダーから取得したメッセージを ehcache キャッシュにキャッシュします。ただし、このキャッシュは無効になっている可能性があります。大きなフォルダーでは無効にすることをお勧めします。

クラスパスのルートに次の内容の「mstor.properties」というテキスト ファイルを作成することで、キャッシュを無効にすることができます。

mstor.cache.disabled=true

この値をシステム プロパティとして設定することもできます。

java -Dmstor.cache.disabled=true SomeProgram
于 2012-07-03T05:05:03.003 に答える
1

VM によって無視される可能性があるため、 System.gc() に依存しないでください。OutOfMemory が発生した場合は、VM が既に GC を実行しようとしたことを意味します。ヒープ サイズを増やしたり、ヒープ内の世代のサイズを変更したりしてみてください (ほとんどのオブジェクトが古い世代になってしまい、若い世代には多くのメモリを必要としないとします)。コードを見直して、参照を保持していないことを確認してください。必要のないリソースに。

于 2012-06-21T17:01:16.763 に答える
0

mstor ライブラリは、メッセージのキャッシュを適切に処理していませんでした。いくつかの調査を行った後、Folder.close()(inbox は上記のフォルダー オブジェクトです) mstor と javaxmail を呼び出すと、getMessage()メソッドの結果としてキャッシュされたすべてのメッセージが解放されることがわかりました。

try/catch ブロックを次のようにしました。

try {
    message = inbox.getMessage(i);
    // moved all of my calls to message.getFrom(),
    // message.getAllRecipients(), etc. inside this try/catch.
} catch (OutOfMemoryError e) {
    if (firstTry) {
        i--;
        firstTry = false;
    } else {
        firstTry = true;
        System.out.println("Message " + i + " skipped.");
    }
    inbox.close(false);
    System.gc();
    inbox.open(Folder.READ_ONLY);
    continue;
}
firstTry = true;

catch ステートメントがヒットするたびに、キャッシュされたメッセージを手動でクリアしてフォルダーを再度開くには、40 ~ 50 ミリ秒かかります。

反復ごとにガベージ コレクターを呼び出すと、1.6 ギガバイトのファイルを解析するのに 57 分かかりました。このロジックでは、同じファイルを解析するのに 18 分しかかかりません。

更新 - mstor が使用するメモリの量を減らすためのもう 1 つの重要な側面は、キャッシュ プロパティにあります。他の誰かが「mstor.cache.disabled」をtrueに設定することについてすでに言及しており、これが役に立ちました。今日、さらに大きなファイルの OOM キャッチの量を大幅に削減する別の重要なプロパティを発見しました。

    Properties props = new Properties();
    props.setProperty("mstor.mbox.metadataStrategy", "none");
    props.setProperty("mstor.cache.disabled", "true");
    props.setProperty("mstor.mbox.cacheBuffers", "false");   // most important
于 2012-06-22T20:42:28.107 に答える