0

テキスト ファイル (>1g) を読み取り、文字列を処理する簡単なコードをいくつか書きました。

ただし、ある時点でメモリ使用量が大きくなっている文字列を(StringBuilderを使用して)追加しようとするため、Javaヒープスペースの問題に対処する必要があります。'-Xmx1024' などを使用してヒープ スペースを増やすことができることはわかっていますが、ここではメモリをほとんど使用しないで作業したいと考えています。以下のコードを変更して操作を管理するにはどうすればよいですか?

私はまだ Java の初心者であり、コードに明らかな間違いを犯した可能性があります。

コード スニペットは次のとおりです。

    private void setInputData() {

    Pattern pat = Pattern.compile("regex");
    BufferedReader br = null;
    Matcher mat = null;

    try {
        File myFile = new File("myFile");
        FileReader fr = new FileReader(myFile);

        br = new BufferedReader(fr);
        String line = null;
        String appendThisString = null;
        String processThisString = null;
        StringBuilder stringBuilder = new StringBuilder();

        while ((line = br.readLine()) != null) {

            mat = pat.matcher(line);

            if (mat.find()) {
                appendThisString = mat.group(1);
            }

            if (line.contains("|")) {
                processThisString = line.replace(" ", "").replace("|", "\t");
                stringBuilder.append(processThisString).append("\t").append(appendThisString);
                stringBuilder.append("\n");
            }
        }
//      doSomethingWithTheString(stringBuilder.toString());
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        try {
            if (br != null)br.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

エラーメッセージは次のとおりです。

スレッド「メイン」の例外 java.lang.OutOfMemoryError: Java ヒープ領域
    java.util.Arrays.copyOf(Arrays.java:2367) で
    java.lang.AbstractStringBuilder.expandCapacity (AbstractStringBuilder.java:130) で
    java.lang.AbstractStringBuilder.ensureCapacityInternal (AbstractStringBuilder.java:114) で
    java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:415) で
    java.lang.StringBuilder.append(StringBuilder.java:132) で
    Test.setInputData(Test.java:47) で
    Test.go (Test.java:18) で
    Test.main (Test.java:13) で
4

7 に答える 7

1

この場合、StringBuilder は使用できません。データをメモリに保持します。結果をすべての行でファイルに保存することを検討する必要があると思います。

つまり、StringBuilder の代わりに FileWriter を使用します。

于 2013-04-16T14:02:00.420 に答える
1

一般的な戦略は、ファイル全体 (またはファイルの大部分) をメモリに保持する必要がないようにアプリケーションを設計することです。

アプリケーションの機能に応じて:

  • 中間データをファイルに書き込み、一度に 1 行ずつ読み込んで処理することができます。
  • 読み取った各行を処理アルゴリズムに渡すことができます。doSomethingWithTheString(...)たとえば、すべての回線ではなく、各回線を個別に呼び出します。

しかし、ファイル全体をメモリに保持する必要がある場合は、岩と困難な場所の間にいます。


注意すべきもう 1 つの点は、StringBuilderlike を使用すると、ファイル サイズの最大 6 倍のメモリが必要になる可能性があることです。こんなふうになります。

  • StringBuilder内部バッファーを拡張する必要がある場合、現在のバッファーの 2 倍のサイズの char 配列を作成し、古いものから新しいものにコピーすることによってこれを行います。その時点で、バッファ拡張が開始される前の 3 倍のバッファ スペースが割り当てられます。ここで、バッファーに追加する文字があと 1 つだけあるとします。

  • ファイルが ASCII (または別の 8 ビット文字セット) の場合、StringBuilderのバッファはその 2 倍の量のメモリを必要とします ... 値で構成されてcharいないためbyteです。

最終的な文字列に含まれる文字数を (ファイル サイズなどから) 適切に見積もっている場合は、StringBuilder. ただし、過小評価してはいけません。

ByteArrayOutputStreamStringBuilder の代わりにバイト指向のバッファ (例: a) を使用して、 // パイプラインで読み取ることもできByteArrayInputStreamます。StreamReaderBufferedReader

しかし、最終的には、大きなファイルをメモリに保持しても、ファイル サイズが大きくなるとスケーリングしません。

于 2013-04-16T14:02:24.510 に答える
1

追加せずにドライランを実行できますが、文字列の合計の長さを数えます。

doSomethingWithTheString がシーケンシャルである場合、他の解決策があります。

文字列をトークン化して、サイズを小さくすることができます。たとえば、Huffman 圧縮は、char を読み取る既存のシーケンスを検索し、可能な限りテーブルを拡張してから、テーブル インデックスを生成します。(オープンソースの OmegaT 翻訳ツールは、トークンの 1 つの場所でそのような戦略を使用します。)したがって、実行したい処理によって異なります。ある種の CSV の読み取りを辞書で見ると、実現可能に思えます。

一般に、データベースを使用します。

PS メモリの半分を節約し、すべてをファイルに書き込んでから、ファイルを 1 つの文字列で再度読み取ることができます。または、メモリ マップ ファイルであるファイルで java.nio ByteBuffer を使用します。

于 2013-04-16T14:07:01.693 に答える
1

あなたの例からは、巨大な文字列を変更した後に何をしようとしているのか明確ではありません。ただし、変更が複数の行にまたがるようには見えないため、変更されたデータを新しいファイルに書き込むだけです。

FileWriterサイクルの前に新しいオブジェクトを作成して開くには、宣言をサイクルの最初にwhile移動し、サイクルの最後に新しいファイルに書き込みます。stringBufferstringBuffer

一方、異なる回線からのデータを組み合わせる必要がある場合は、データベースの使用を検討してください。どの種類かは、データの性質によって異なります。レコードのような組織がある場合は、Apache DerbyMySQLなどのリレーショナル データベースを採用することができます。そうでない場合は、 CassandraMongoDBなどのいわゆる No SQL データベースをチェックアウトすることもできます。

于 2013-04-16T14:04:36.653 に答える
0

ファイルに行末記号が含まれていますか? そうでない場合、while ループはループし続け、エラーにつながります。その場合、リーダーが無限に大きくならないように、一度に一定のバイト数を読み取ってみる価値があるかもしれません。

于 2013-04-16T14:12:32.190 に答える