5

長さ 3 から 6 の単語を含む辞書と、単語 7 を含む辞書の 2 つのファイルがあります。単語は、改行で区切られたテキストファイルに保存されます。このメソッドはファイルをロードし、アプリケーション クラスに保存する arraylist に挿入します。

ファイルサイズは 386KB と 380KB で、それぞれ 200k ワード未満です。

private void loadDataIntoDictionary(String filename) throws Exception {
    Log.d(TAG, "loading file: " + filename);
    AssetFileDescriptor descriptor = getAssets().openFd(filename);
    FileReader fileReader = new FileReader(descriptor.getFileDescriptor());
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    String word = null;

    int i = 0;

    MyApp appState = ((MyApp)getApplicationContext());

    while ((word = bufferedReader.readLine()) != null) {
        appState.addToDictionary(word);
        word = null;
        i++;
    }
    Log.d(TAG, "added " + i + " words to the dictionary");

    bufferedReader.close();
}

プログラムは、2.3.3 を実行しているエミュレーターで 64MB の SD カードを使用してクラッシュします。logcat を使用して報告されているエラー。ヒープが 24 MB を超えて大きくなります。次に、クランプ ターゲット GC ヒープ25.XXXが 24.000 MB にあることがわかります。

GC_FOR_MALLOC は 0K を解放し、12% を解放し、外部 1657k/2137K を一時停止し、208ms を一時停止しました。
GC_CONCURRENT が XXK を解放し、14% が解放されまし
た。

このような大きなヒープを取得せずにこれらのファイルをロードするにはどうすればよいですか?

MyApp 内:

private ArrayList<String> dictionary = new ArrayList<String>();
public void addToDictionary(String word) {
    dictionary.add(word);
}
4

1 に答える 1

1

他の問題やバグに関係なく、ArrayListこの種のストレージでは非常に無駄になる可能性があります。成長する ArrayList がスペースを使い果たすと、その下にあるストレージ配列のサイズが 2 倍になるためです。そのため、ストレージのほぼ半分が無駄になる可能性があります。ストレージ配列または ArrayList を正しいサイズに事前にサイズ設定できる場合は、大幅な節約が得られる可能性があります。

また、(偏執的なデータクレンジング帽子をかぶって)入力ファイルに余分な空白がないことを確認してください-String.trim()必要に応じて各単語に使用するか、最初に入力ファイルをクリーンアップできます. しかし、あなたが言及したファイルサイズを考えると、これは重大な問題になるとは思いません.

入力がテキスト自体を保存するのに2MB未満かかると予想します(Javaは内部でUTF-16を使用するため、通常は1文字あたり2バイトかかることを思い出してください)が、Stringオブジェクト参照にはおそらく1.5MBのオーバーヘッドと1.5MBがかかります文字列の長さのオーバーヘッド、およびオフセットとハッシュコードの場合はおそらく何度も同じです(String.javaを見てください)... 24MBのヒープはまだ少し過剰に聞こえますが、近くにいる場合はそう遠くありません- 不運な ArrayList のサイズ変更の効果が 2 倍になります。

実際、憶測ではなく、テストはどうでしょうか。次のコードは、-Xmx24Mストールする前に約 560,000 個の 6 文字の文字列を取得して実行します (Java SE 7 JVM、64 ビット上)。最終的には約 580,000 までクロールします (多くの GC スラッシングがあると思います)。

    ArrayList<String> list = new ArrayList<String>();
    int x = 0;
    while (true)
    {
        list.add(new String("123456"));
        if (++x % 1000 == 0) System.out.println(x);
    }

だから私はあなたのコードにバグがあるとは思わない-多数の小さな文字列を格納することはJavaではあまり効率的ではない-上記のテストでは、すべてのオーバーヘッドのために1文字あたり7バイト以上かかる(32-ビットおよび 64 ビット マシン、ちなみに、JVM 設定にも依存します)!

文字列の ArrayList ではなく、バイト配列の配列を格納することで、わずかに良い結果が得られる場合があります。Triesなど、文字列を格納するためのより効率的なデータ構造もあります。

于 2012-10-28T23:05:07.867 に答える