0

私は自分で書いたJavaアプリケーションを持っています-小さなメールモニターです。これは、定期的に別の場所に入力されるテーブルを持つMySQLデータベースで機能します。テーブルを調べて、レコードがテーブルに表示されるときにメールを送信します。

私の問題は、アプリケーションがメモリリークを起こすことです。私が使用するすべてのスコープが消えて、使用されているすべてのオブジェクトがゴミ収集可能になるので、そうではないと確信していました。しかし、しばらくすると(渡した-Xmxに応じて)、アプリケーションはOutOfHeapSpaceエラーで停止します。

コード全体を投稿することはできません。これは私のものではないためですが、擬似コードを使用してコードを再作成しようとしました。

Main:
  Startup
  Create .lock file (FileChannel)
  Instantiate Main Class

Constructor Main:
  Class.ForName for the MySQL driver
  Read properties file (settings)
  Create connection object (MySQL)
  Fetch unsent mail ids (ArrayList)
  while(true)
    while have more mail ids
      new Thread(Top Mail ID, MySQL Connection object, Sleep Time, Blacklist);
    end while
    if have no more mail ids in ArrayList:
      sleep for a number of seconds (usually 300)
    end if
  end while

Constructor Thread:
  Prepare Statement
  New Thread(this).start();
  Sleep

Thread run():
  Select Record by passed Mail ID
  Extract everything (Sender, Receiver, Subject etc.)
  Check Blacklist, return if matched
  Extract Attachments as blobs

私がこれまでに試したこと:

jvisualvmは、メモリが時間の経過とともにどのように変化するかを示してくれました。私が見ているのは、ヒープ内のギザギザの線です。メモリの割り当てと収集は定期的に行われますが、収集後は、最後の収集後よりも常に少し多くのメモリが割り当てられます。スレッドの数は問題ないようですが、常に標準の数になります。

jvisualvmの情報量は私には多すぎます。そこにリストされている識別できないスレッドがあり、作成したスレッドはクラスとしてリストされていないため、「私の」コードを正確に判別することは困難です。

誰かが私の擬似コードのマルチスレッドの一般的な間違いを認識したり、リークを簡単に特定できるツールを推奨したりできますか?

ありがとうございました。

編集1:すべてのスレッドに渡される接続オブジェクトを介したデータアクセスは、それ自体で同期されます。

編集2:送信できないメールの数を確認しました(これらはデータベースに残っているため)。約10通あります。

編集3:Eclipse Memory Analyzerを見つけてインストールしたところ、問題が示唆されました。1つの接続オブジェクトを保持しながらPreparedStatementsを使用すると、その内部のこの接続で実行されるすべてのPreparedStatementsのHashMapが保持されるため、選択されたすべてのデータが追加されるようです。私はPreparedStatementが範囲外になり、収集されることに依存していました。これで問題が解決するかどうか、書き直して試してみます。

4

2 に答える 2

1

擬似コードからは、送信する電子メールごとにスレッドを作成しているように見えますが、これはそれほど効率的ではないようです。スレッドの数は「正常」であるとおっしゃっていましたが、コードはそうではないことを示しているようです。代わりにスレッドプールを使用してみてください。スレッドワーカーの数が限られており、ある種のジョブキューを介してスレッドワーカーに作業を渡します。

スレッドが完了しない、またはスレッドが「ハング」したままになる可能性のあるエラーがあるかどうかを知っていますか?これはメモリリークの可能性があるため、エラー処理コードを調べてください。

過去に私はjprofilerある程度の成功を収めて使用しましたが、おそらくそれは有用な代替手段になるでしょう。

于 2012-06-11T09:27:50.047 に答える
0

これが誰かに興味があるなら、私はメモリの問題を修正しました。

アプリケーションは実際にはメモリをリークしませんでした。同じ接続オブジェクトを渡したので、それに対して実行されたすべてのプリペアドステートメントの結果が大きなハッシュマップに保存されたようです。そのため、メモリ使用量は常に増加していました。これは、jvisualvmを使用して作成され、Eclipseメモリアナライザーにロードされたヒープダンプを使用して見つけました。

ThreadPool(Cached)を使用するようにアプリケーションを書き直し、すべてのスレッドに専用の接続を開き、すべてのスレッドの終わりで接続を閉じました。

于 2012-06-15T11:53:02.787 に答える