Java Android将单词插入ArrayList,内存不足

Java Android将单词插入ArrayList,内存不足,java,android,out-of-memory,Java,Android,Out Of Memory,我有两个文件,一个包含单词长度3到6的字典和一个包含单词长度7的字典。这些单词存储在文本文件中,用换行符分隔。此方法加载文件并将其插入我存储在应用程序类中的arraylist中 文件大小分别为386KB和380KB,每个文件包含不到200k个字 private void loadDataIntoDictionary(String filename) throws Exception { Log.d(TAG, "loading file: " + filename); AssetF

我有两个文件,一个包含单词长度3到6的字典和一个包含单词长度7的字典。这些单词存储在文本文件中,用换行符分隔。此方法加载文件并将其插入我存储在应用程序类中的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报告的错误。 堆增长超过24MB。然后我看到将目标GC堆从
25.XXX
压缩到24.000 MB

GC_为_MALLOC释放0K,12%自由,外部1657k/2137K,暂停208ms。
GC_并发释放XXK,14%释放
24字节分配内存不足,然后出现致命异常,内存耗尽

如何加载这些文件而不获得如此大的堆

MyApp内部:

private ArrayList<String> dictionary = new ArrayList<String>();
public void addToDictionary(String word) {
    dictionary.add(word);
}
private ArrayList dictionary=new ArrayList();
public void addToDictionary(字符串字){
添加(单词);
}

无论是否存在任何其他问题/错误,
ArrayList
对于此类存储来说都是非常浪费的,因为随着ArrayList的不断增长,其空间将耗尽,其底层存储阵列的大小将翻一番。因此,有可能近一半的存储空间被浪费了。如果您可以将存储阵列或ArrayList的大小预调整为正确的大小,则可能会节省大量资源

另外(戴上paranoid data Cleaning帽子)确保输入文件中没有多余的空格-如果需要,可以在每个单词上使用
String.trim()
,或者先清理输入文件。但考虑到您提到的文件大小,我认为这不是一个重大问题

我希望您的输入存储文本本身所需的开销小于2MB(请记住,Java在内部使用UTF-16,因此每个字符通常需要2个字节),但字符串对象引用可能需要1.5MB的开销,字符串长度可能需要1.5MB的开销,偏移量和哈希代码也可能需要同样的开销(看一看)…虽然24MB堆的大小听起来仍然有点过大,但如果您正在获得不幸的ArrayList重新调整大小所带来的几乎翻倍的效果,那就不远了

事实上,与其猜测,不如做一个测试。下面的代码,在暂停之前(在JavaSE7JVM上,64位),使用
-Xmx24M
运行大约560000个6字符的字符串。它最终会爬升到大约580000个(我想会有很多GC抖动)

ArrayList list=new ArrayList();
int x=0;
while(true)
{
添加(新字符串(“123456”);
如果(++x%1000==0)System.out.println(x);
}
因此,我认为您的代码中没有bug——在Java中存储大量小字符串不是很有效——因为上面的测试每个字符需要超过7个字节,因为所有的开销(顺便说一句,32位和64位机器可能不同,并且也取决于JVM设置)


通过存储字节数组而不是字符串数组列表,您可能会获得更好的结果。还有更有效的数据结构用于存储字符串,例如。

为什么这样做:word=null?我试图确保gc知道释放该单词。我只是作为最后的手段添加了它。您可以添加的代码吗ode>addToDictionary()函数?这是查找内存泄漏的最明显的地方。看起来word=null导致了无限循环。请取出并运行它。@DanHulme我在最后添加了代码。
    ArrayList<String> list = new ArrayList<String>();
    int x = 0;
    while (true)
    {
        list.add(new String("123456"));
        if (++x % 1000 == 0) System.out.println(x);
    }