15

2,000 万行のテキストを含む大きなテキスト ファイルがあります。次のプログラムを使用してファイルを読み取ると、問題なく動作し、実際、メモリの問題なしで、より大きなファイルを読み取ることができます。

public static void main(String[] args) throws IOException {
    File tempFile = new File("temp.dat");
    String tempLine = null;
    BufferedReader br = null;
    int lineCount = 0;
    try {
        br = new BufferedReader(new FileReader(tempFile));
        while ((tempLine = br.readLine()) != null) {
            lineCount += 1;
        }
    } catch (Exception e) {
        System.out.println("br error: " +e.getMessage());
    } finally {
        br.close();
        System.out.println(lineCount + " lines read from file");
    }
}

ただし、このファイルを読み取る前にいくつかのレコードを追加する必要がある場合、BufferedReader は大量のメモリを消費します (これを監視するために Windows タスク マネージャーを使用しましたが、あまり科学的ではありませんが、問題を示しています)。修正されたプログラムを以下に示します。最初のプログラムと同じですが、最初に単一のレコードをファイルに追加しています。

public static void main(String[] args) throws IOException {
    File tempFile = new File("temp.dat");
    PrintWriter pw = null;
    try {
        pw = new PrintWriter(new BufferedWriter(new FileWriter(tempFile, true)));
        pw.println(" ");
    } catch (Exception e) {
        System.out.println("pw error: " + e.getMessage());
    } finally {
        pw.close();
    }

    String tempLine = null;
    BufferedReader br = null;
    int lineCount = 0;
    try {
        br = new BufferedReader(new FileReader(tempFile));
        while ((tempLine = br.readLine()) != null) {
            lineCount += 1;
        }
    } catch (Exception e) {
        System.out.println("br error: " +e.getMessage());
    } finally {
        br.close();
        System.out.println(lineCount + " lines read from file");
    }
}

Windows タスク マネージャーのスクリーンショット。2 番目のバージョンのプログラムを実行したときの行の大きな隆起がメモリ消費量を示しています。

タスクマネージャーのスクリーンショット

そのため、メモリ不足になることなくこのファイルを読み取ることができました。しかし、5,000 万を超えるレコードを含むはるかに大きなファイルがあり、それらに対してこのプログラムを実行すると、メモリ不足の例外が発生しますか? プログラムの最初のバージョンはどのようなサイズのファイルでも問題なく動作するのに、2 番目のプログラムは動作が大きく異なり、失敗に終わる理由を誰か説明できますか? 私はWindows 7で次を実行しています:

Java バージョン "1.7.0_05"
Java(TM) SE ランタイム環境 (ビルド 1.7.0_05-b05)
Java HotSpot(TM) クライアント VM (ビルド 23.1-b03、混合モード、共有)

4

6 に答える 6

1

VM-Optionsで Java-VM を起動できます

-XX:+HeapDumpOnOutOfMemoryError

これにより、リークの疑いを見つけるために分析できるヒープダンプがファイルに書き込まれます

オプションを追加するには「+」を使用し、オプションを削除するには「-」を使用します。

Eclipse を使用している場合は、Java メモリ アナライザー プラグインMATを使用して、実行中の VM からヒープ ダンプを取得し、リーク容疑者などの優れた分析を行います。

于 2012-09-11T21:38:25.557 に答える
0
     pw = new PrintWriter(new BufferedWriter(new FileWriter(tempFile, true)));

BufferedWriter を使用しないようにしましたか? 最後に数行を追加する場合、おそらくバッファは必要ありませんか? その場合は、バイト配列 (コレクションまたは文字列ビルダー) の使用を検討してください。最後に、Java 1.6_32 で同じことを試しましたか? いずれかのライターの新しいバージョンのバグである可能性があります。

pw.close(); の前後に空きメモリを出力できますか? ?

System.out.println("before wr close :"  + Runtime.getRuntime().freeMemory());

閉じた後とリーダーを閉じた後も同様

于 2012-09-09T11:07:09.300 に答える
0

ファイルに改行/改行がまったくない可能性があるためです。この場合、readLine()おそらくメモリが不足しているファイルから単一の文字列を作成しようとします。

readLine() の Java ドキュメント:

Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.

于 2012-09-09T11:13:25.410 に答える
0

やってみました:

A) 読み取りに使用する新しい File インスタンスを作成しますが、同じファイルを指します。B) 2 番目の部分でまったく異なるファイルを読み取る。

File オブジェクトがまだ何らかの形で PrintWriter にアタッチされているのか、それとも OS がファイル ハンドルで何かおかしなことをしているのか、疑問に思っています。これらのテストは、どこに焦点を合わせるべきかを示しているはずです。

これはコードに問題があるようには見えず、壊れてはならないというあなたの論理は正しいように思われるので、根本的な機能である必要があります。

于 2012-09-10T15:56:57.143 に答える
0

java 次の Java ルーチンを実行するたびに、まったく新しいオブジェクトが作成されます。

tempLine = br.readLine()

readLine() を呼び出すたびに、おそらく新しい String オブジェクトが作成され、再割り当てが呼び出されて値が tempLine に割り当てられるたびにヒープに残されると思います。

したがって、GC は常に呼び出されるわけではないため、数秒以内に何千ものオブジェクトがヒープに残される可能性があります。

System.gc() を1000行ごとに呼び出すのは悪い考えだと言う人もいますが、それで問題が解決するかどうか知りたいです。また、各行の後にこのコマンドを実行して、基本的に各オブジェクトをガベージ コレクション可能としてマークすることもできます。

tempLine=null;
于 2012-08-30T23:44:50.123 に答える
-3

より大きなヒープで Java を起動する必要があります。java コマンドのパラメーターとして -Xmx1024m を試してください。

基本的に、ファイルのサイズよりも多くのメモリが必要になります。

于 2012-08-30T17:31:07.717 に答える