2011-08-04 17 views
4

私は約50000レコードを選択するのにMySQLとJavaを使用しています。 奇妙なことに、ResultSetとnext()メソッドを使用してデータを読み込むと、フェッチ中にJavaアプリケーションのRAM使用量が増加することがわかります。それは255 MBから始まり、379 MBまで増加します!私が使用しています コードはここにある:mysqlメモリ(RAM)の使用量がResultSet使用中に増加しますか?

try { 
    Class.forName("com.mysql.jdbc.Driver"); 
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/#mysql50#crawler - used in report?" + "user=root&password=&useUnicode=true&characterEncoding=UTF-8"); 
    Statement st = conn.createStatement(); 
    ResultSet rsDBReader = st.executeQuery("SELECT Id, Content FROM DocsArchive"); 
    while (rsDBReader.next()) { 
     int docId = rsDBReader.getInt(1); 
     String content = rsDBReader.getString(2); 
     . . . 
     } 
    rsDBReader.close(); 
    st.close(); 
    conn.close(); 
} catch (Exception e) { 
    System.out.println("Exception in reading data: " + e); 
} 

私はメモリ使用量は、ResultSetのためのプログラムの他の部分ではないことを確信しています。 このプログラムではレコードを更新する必要はないので、作業を終えたらすべてのレコードを削除したいと思っています。 私の推測では、読み込まれたレコードは削除されず、プログラムはメモリを解放しないということです。そこで私は、次のコードを使用するなど、これを避けるためにいくつかのトリックを使用しました:

Statement st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT); 

st.setFetchSize(500); 
rsDBReader.setFetchSize(500); 

しかし、それらは変更されませんでした。 :(

だから私は、読み込まれた行の(リリース)のメモリを削除するいくつかの方法が必要です。

もう一つの興味深い点は、機能を仕上げとResultSetの、ステートメントとの接続を閉じ、そしてに行くにも後ということですプログラムの他の部分は、まだプログラムのメモリ使用量は減少しません! おかげ

+2

MySQLはクエリの結果をキャッシュします。 – Johan

+0

ありがとう、しかし、私はどのようにキャッシュを空にすることができますか? – Soheil

+0

メモリを節約するために何ができるのですが、SELECT文ごとに得られる結果の数が制限されます – RMT

答えて

2

私はあなたのクエリで取得する行の量を制限することをお勧めしたい。50000はたくさんあるのに、なぜループを持っていません毎回1000行をフェッチします。

hereのように、limitステートメントを使用してこれを達成できます。処理しているデータの量については実用的であることが常にベストです。あなたの現在の選択は今日50000行を返すかもしれませんが、もし明日100万人に成長すればどうなりますか?アプリケーションがチョークします。だから、ステップごとに処理を行います。

6

Statement.setFetchSize()を使用して、特定の数の行を含むストリームについてResultSetをストリーミングする必要があることをヒントとしてドライバに通知します。私の知る限り、MySQL Connector-JドライバはヒントとストリームResultSetを理解しています(ただしこれはMySQLの場合は一度に1行に制限されています)。

デフォルト値が0の場合、Connector-Jドライバはストリーミングせずに完全なResultSetをフェッチします。そのため、MySQLの場合は明示的な値Integer.MIN_VALUEを指定する必要があります。

声明:

Statement st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT); 

は(少なくともそれ自身のアコードに)ResultSetをストリーミングにはなりません。これは、結果セットが「スクロール可能」ではなく(すなわち、順方向にのみトラバースできる)、「更新可能」ではないことを保証し、トランザクションがコミットするときに下にあるカーソルを閉じる。

JDBC implementation notes of MySQLに述べたように、(ResultSet.CLOSE_CURSORS_AT_COMMITパラメータなし)上記のステートメントは、行毎に発生するストリーミングStatement.setFetchSize(Integer.MIN_VALUE)呼び出しと一緒に呼び出されなければなりません。そのようなシナリオに関連する警告も文書化されています。

カーソルの保持機能は、MySQLのマニュアルに記載されている例では指定されていません。Connection.getHoldability()で提供されている値と異なる値が必要な場合は、このアドバイスは適用されない可能性があります。

+0

いいえ、MySQL JDBCドライバはフェッチサイズを制御するために非常に限られたサポートしかありません。デフォルトの動作では、selectの結果全体を一度に取得します。結果をストリーミングするには、フェッチサイズをInteger.MIN_VALUEに設定し、StatementをResultSet.TYPE_FORWARD_ONLY、ResultSet.CONCUR_READ_ONLYで作成する必要があります。これを行うには、ドキュメントに記載されているように、いくつかのさらなる制限が考慮されなければなりません:http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html – jarnbjo

+0

私はこれらを使用しましたコード: st.setFetchSize(500); rsDBReader.setFetchSize(500); しかし、それは何も変わっていません – Soheil

+0

@jarnbjo、ありがとう。私は部分的にしか正しいとは言えませんでした。 –

-1

実際には予想される動作であり、必ずしもメモリリークを示す必要はありません。オブジェクトインスタンスは、到達不能になった直後にJavaでガベージコレクションされず、大部分のJava VMは割り当てられたメモリをオペレーティングシステムに戻すことを非常に嫌っています。

あなたはOracleのJava VMの最近のバージョンを使用して、実際に、より積極的なガベージコレクタを必要とする場合は、javaコマンドに次の引数を追加することにより、G1GC実装を試すことができます。

-XX:+ UnlockExperimentalVMOptions - G1GCガベージコレクタは、通常、デフォルトのガベージコレクタよりも高速にオブジェクトを再利用し、未使用のメモリもプロセスによって解放されます。

0

Postgresの最新リリースで同様の問題が存在することに注意してください。カーソル処理を実現するためには、connection.setAutoCommit(false)のコネクションで自動コミットを無効にし、SQL文(つまり、セミコロンを1つだけ含む文)で単一の文を使用する必要があります。それは私のために働いた。

Postgres JDBC documentation

関連する問題