【问题标题】:OutOfMemoryError when reading more than one asset读取多个资产时出现 OutOfMemoryError
【发布时间】:2014-07-27 06:57:17
【问题描述】:

我有一个读取资产文件并将其内容存储在数组中的函数。这是一个代码。

private List<String> infinitivesDB;
private List<String> formsDB;

private void getDB(String lang, String type)
    {
        while(true)
        {
        try {
            String infinitivesDB = getStringFromAssetFile(this, "inf.txt");
            this.infinitivesDB = new LinkedList(Arrays.asList(infinitivesDB.split("\n")));
            break; //would be needed if it was reachable
        } catch (IOException e) {           
            e.printStackTrace();
            }
    }
        while(true)
        {
        try {
            String formsDB = getStringFromAssetFile(this, "forms.txt");//EXCEPTION HERE?        
            this.formsDB = new LinkedList(Arrays.asList(formsDB.split("\n")));
            break; //would be needed if it was reachable
        } catch (IOException e) {           
            e.printStackTrace();
            }
    }
    }

文件“inf.txt”为 58kb。文件“forms.txt”为 3.5 Mb。

每当我尝试运行此代码时,它都会引发 OutOfMemoryError。这是日志

07-27 02:40:13.399: E/AndroidRuntime(2017): FATAL EXCEPTION: main
07-27 02:40:13.399: E/AndroidRuntime(2017): Process: com.highstaker.formdictionary, PID: 2017
07-27 02:40:13.399: E/AndroidRuntime(2017): java.lang.OutOfMemoryError
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.expandBuffer(Scanner.java:2067)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.readMore(Scanner.java:2031)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.findDelimiterAfter(Scanner.java:2009)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.setTokenRegion(Scanner.java:1923)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.hasNext(Scanner.java:541)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.hasNext(Scanner.java:519)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.convertStreamToString(MainActivity.java:411)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.getStringFromAssetFile(MainActivity.java:404)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.getDB(MainActivity.java:389)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.onCreate(MainActivity.java:50)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.Activity.performCreate(Activity.java:5243)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.access$700(ActivityThread.java:135)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.os.Handler.dispatchMessage(Handler.java:102)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.os.Looper.loop(Looper.java:137)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.main(ActivityThread.java:4998)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.lang.reflect.Method.invokeNative(Native Method)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.lang.reflect.Method.invoke(Method.java:515)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at dalvik.system.NativeStart.main(Native Method)
07-27 02:40:15.179: I/Process(2017): Sending signal. PID: 2017 SIG: 9

但是,如果我尝试只读取一个文件(通过注释掉其中一个 while 循环及其内容),无论我选择哪个文件 - 它都可以正常工作。如果我尝试在两个循环中读取“inf.txt”(较小)并存储在这些不同的数组中,它也可以正常工作。

此外,如果我只是切换“whiles”,因此在较小的文件之前读取较大的文件,它也可以工作!

我想知道为什么会这样。另外,如果两个资产文件都很大,我如何读取它们?我试图在两个循环中读取“forms.txt”(更大),它引发了同样的错误(我真的预料到了)。

【问题讨论】:

  • 我怀疑错误来自此代码。请看sscce.org
  • 问题更多出在getStringFromAssetFile这个方法上,能不能把它的代码贴出来。

标签: java android file-io out-of-memory android-assets


【解决方案1】:

导致错误是因为您试图从 3.5 mb 文件中加载大量字符串中的数据。为此目的的快速解决方案是

您可以使用 android:largeHeap="true" 请求更大的堆大小,但这不适用于任何预 Honeycomb 设备。在 2.3 之前的设备上,您可以使用 VMRuntime 类,但这不适用于 Gingerbread 及更高版本。

获得尽可能大的限制的唯一方法是通过 NDK 执行内存密集型任务,因为 NDK 不像 SDK 那样施加内存限制。

或者,您可以仅加载当前视图中的模型部分,并根据需要加载其余部分,同时从内存中删除未使用的部分。但是,这可能是不可能的,具体取决于您的应用。

在 API 级别 11+ 上运行的应用程序可以在清单中的元素上设置 android:largeHeap="true" 以请求大于正常的堆大小,并且 ActivityManager 上的 getLargeMemoryClass() 将告诉您该堆有多大.然而:

这仅适用于 API 级别 11+(即 Honeycomb 及更高版本)

无法保证大堆有多大

用户会感知到你的大堆请求,因为它会强制他们的其他应用程序退出 RAM,终止其他应用程序的进程以释放系统 RAM 供你的大堆使用

由于#3,以及我预计 android:largeHeap 将被滥用的事实,将来可能会放弃对此的支持,或者可能会在安装时警告用户(例如,您需要申请特别许可)

目前,此功能的文档很少

【讨论】:

    【解决方案2】:

    我可以在这里看到几个糟糕的设计决策。最重要的是:如果您需要一个文件作为字符串列表,为什么不直接读取它呢?

    使用扫描仪读取流,这本身不是必需的。其他 io 类可以让您阅读行而没有这种复杂性。

    looong 字符串被拆分成一个大数组,最终生成列表。因此,您在内存中的文件至少一式三份。

    LinedList 可以有效地增长,但这里不需要它。 ArrayList 更节省空间。

    getStringFromAssetFile 必须重新设计。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 2014-09-30
      • 1970-01-01
      相关资源
      最近更新 更多