6

現在、私のプログラムにはメモリの問題があり、アプリを確認したところ、String.split()メソッドが大量のメモリを使用していることがわかりました。を使用してみましたStreamTokenizerが、これにより事態がさら​​に複雑になるようです。

メソッドよりも少ないメモリを使用して、longStringsを smallに分割するより良い方法はありますか?StringsString.split()

4

4 に答える 4

1

split の現実的な使用が「大量のメモリを消費する」可能性はほとんどありません。入力は非常に大きく (何メガバイトも) ある必要があり、結果が何百万もの部分に分割されて、気付かれることさえあります。

以下は、約 180 万文字のランダムな文字列を作成し、それを 100 万を超える文字列に分割して、使用メモリと所要時間を出力するコードです。

ご覧のとおり、それほど多くはありません。わずか 350 ミリ秒で 61Mb が消費されています。

public static void main(String[] args) throws Exception {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 99999; i++) {
        sb.append(Math.random());
    }
    long begin = System.currentTimeMillis();
    String string = sb.toString();
    sb = null;
    System.gc();
    long startFreeMem = Runtime.getRuntime().freeMemory();
    String[] strings = string.split("(?=[0-5])");
    long endFreeMem = Runtime.getRuntime().freeMemory();
    long execution = System.currentTimeMillis() - begin;

    System.out.println("input length = " + string.length() + "\nnumber of strings after split = " + strings.length + "\nmemory consumed due to split = "
            + (startFreeMem - endFreeMem) + "\nexecution time = " + execution + "ms");
}

出力 (かなり一般的な Windows ボックスで実行):

input length = 1827035
number of strings after split = 1072788
memory consumed due to split = 71740240
execution time = 351ms

興味深いことに、メモリを使用しない System.gc()場合は約 1/3 でした。

memory consumed due to split = 29582328
于 2012-08-09T13:21:29.390 に答える
0

Split はまったく新しい文字列を作成しません。substring内部的に使用Stringして、元の文字列の正しい部分文字列を指す新しいオブジェクトを作成しますchar[]

したがって、オブジェクト作成の (わずかな) オーバーヘッドを除けば、メモリの観点からは大きな影響はありません。

ps:StringTokenizer同じ手法を使用するため、おそらく分割と同じ結果が得られます。

編集

これが事実であることを確認するには、以下のサンプル コードを使用できます。元の文字列と分割された文字列の基になる部分を分割して出力します。出力は、それらがすべて同じであることを示してabc,defいますabcdefchar[]

出力:

Reference: [C@3590ed52  Content: [a, b, c, ,, d, e, f]
Reference: [C@3590ed52  Content: [a, b, c, ,, d, e, f]
Reference: [C@3590ed52  Content: [a, b, c, ,, d, e, f]

コード:

public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    String s = "abc,def";
    String[] ss = s.split(",");
    Field f = String.class.getDeclaredField("value");
    f.setAccessible(true);
    System.out.println("Reference: " + f.get(s) + "\tContent: " + Arrays.toString((char[])f.get(s)));
    System.out.println("Reference: " + f.get(ss[0]) + "\tContent: " + Arrays.toString((char[])f.get(ss[0])));
    System.out.println("Reference: " + f.get(ss[1]) + "\tContent: " + Arrays.toString((char[])f.get(ss[1])));
}
于 2012-08-09T12:54:09.003 に答える
0

長い文字列の 1 つまたはいくつかの配列を使用するだけの場合は、アスペクト メモリを分割することもできます。長い文字列は常にメモリ内にあります。お気に入り

private static List<String> headlist = new ArrayList<String>();

String longstring = ".....";
headlist.add(longstring.split(" ")[0]);

長い文字列が常にメモリ内にあるよりも。JVM はそれを gc できません。

この状況では、多分あなたは試すことができると思います

private static List<String> headlist = new ArrayList<String>();

String longstring = ".....";
headlist.add(new String(longstring.split(" ")[0]));

次のコードのように

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class SplitTest {
    static Random rand = new Random();
    static List<String> head = new ArrayList<String>();

    /**
     * @param args
     */
    public static void main(String[] args) {
        while(true) {
            String a = constructLongString();
            head.add(a.split(" ")[0]); //1
            //head.add(new String(a.split(" ")[0])); //2
            if (i % 1000 == 0)
                System.out.println("" + i);
            System.gc();
        }
    }

    private static String constructLongString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            sb.append(rand.nextInt(10));
        }
        sb.append(" ");
        for (int i = 0; i < 4096; i++) {
            sb.append(rand.nextInt(10));
        }
        return sb.toString();
    }
}

-Xmx60Mで実行すると、約6000以上のメモリが不足し、コード行2を使用する場合、行1をコメントすると、6000よりも大きく長時間実行されます

于 2012-08-09T13:08:28.543 に答える
0

ある種のストリーム リーダーを使用し、大きなデータ文字列でメモリを乱用しないようにする必要があります。ここにいくつかの例:

 public static void readString(String str) throws IOException {
        InputStream is = new ByteArrayInputStream(str.getBytes("UTF-8"));

        char[] buf = new char[2048];
        Reader r = new InputStreamReader(is, "UTF-8");

        while (true) {
            int n = r.read(buf);
            if (n < 0)
                break;

            /*
             StringBuilder s = new StringBuilder();
             s.append(buf, 0, n);
             ... now you can parse the StringBuilder ...  
            */
        }
    }
于 2012-08-09T13:25:17.377 に答える