Java Android将单词插入ArrayList,内存不足
我有两个文件,一个包含单词长度3到6的字典和一个包含单词长度7的字典。这些单词存储在文本文件中,用换行符分隔。此方法加载文件并将其插入我存储在应用程序类中的arraylist中 文件大小分别为386KB和380KB,每个文件包含不到200k个字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
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知道释放该单词。我只是作为最后的手段添加了它。您可以添加
ArrayList<String> list = new ArrayList<String>();
int x = 0;
while (true)
{
list.add(new String("123456"));
if (++x % 1000 == 0) System.out.println(x);
}