【问题标题】:Android: Out of Memory error StringBuilderAndroid:内存不足错误StringBuilder
【发布时间】:2013-11-01 11:38:33
【问题描述】:

在我的应用程序中,我以 JSON 的形式从服务器获取数据。数据约为 1.5 MB。该应用程序可以运行,但有时它会在从服务器获取数据时崩溃,并给出 OutOfMemoryError。

这是我的方法:

private String sendPostRequest(String url, List<NameValuePair> params)
        throws Exception {
    String ret = null;

    BufferedReader bufferedReader = null;
    HttpClient httpClient = new DefaultHttpClient();
    HttpPost request = new HttpPost(url);

    try {
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params);
        request.setEntity(entity);

        HttpResponse response = httpClient.execute(request);

        bufferedReader = new BufferedReader(new InputStreamReader(response
                .getEntity().getContent()));
        StringBuilder stringBuilder = new StringBuilder("");
        StringBuilder stringBuilder2;
        String line = "";
        //String LineSeparator = System.getProperty("line.separator");
        while ((line = bufferedReader.readLine()) != null) {
            stringBuilder.append(line);
        }
        bufferedReader.close();

        ret = stringBuilder.toString();
        stringBuilder = null;

    } catch (ClientProtocolException e) {
        e.printStackTrace();
        // TODO: report an error
    } catch (IOException e) {
        e.printStackTrace();
        // TODO: report an error
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
                // TODO Auto-generated catch block
            }
        }
    }
    return ret;
}

我尝试在使用后将 StringBuilder 设置为 null,但没有帮助。 我在下面发布 logcat 跟踪:

10-23 03:39:25.271: E/dalvikvm-heap(1011): Out of memory on a 4116282-byte allocation.
10-23 03:39:25.271: I/dalvikvm(1011): "AsyncTask #1" prio=5 tid=11 RUNNABLE
10-23 03:39:25.271: I/dalvikvm(1011):   | group="main" sCount=0 dsCount=0 obj=0x417c5e88 self=0x2a1d1db8
10-23 03:39:25.271: I/dalvikvm(1011):   | sysTid=1025 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=706552328
10-23 03:39:25.271: I/dalvikvm(1011):   | state=R schedstat=( 3053328788 23773807212 6541 ) utm=241 stm=64 core=0
10-23 03:39:25.271: I/dalvikvm(1011):   at java.lang.String.<init>(String.java:~422)
10-23 03:39:25.271: I/dalvikvm(1011):   at java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:642)
10-23 03:39:25.282: I/dalvikvm(1011):   at java.lang.StringBuilder.toString(StringBuilder.java:663)
10-23 03:39:25.282: I/dalvikvm(1011):   at com.dzo.redacted.driverapp.datalayer.ServerConnect.sendPostRequest(ServerConnect.java:128)
10-23 03:39:25.282: I/dalvikvm(1011):   at com.dzo.redacted.driverapp.datalayer.ServerConnect.Sync(ServerConnect.java:61)
10-23 03:39:25.282: I/dalvikvm(1011):   at com.dzo.redacted.driverapp.datalayer.DBSync.Login(DBSync.java:59)
10-23 03:39:25.282: I/dalvikvm(1011):   at com.dzo.redacted.driverapp.asynctask.SyncDBTask.doInBackground(SyncDBTask.java:52)
10-23 03:39:25.282: I/dalvikvm(1011):   at com.dzo.redacted.driverapp.asynctask.SyncDBTask.doInBackground(SyncDBTask.java:1)
10-23 03:39:25.282: I/dalvikvm(1011):   at android.os.AsyncTask$2.call(AsyncTask.java:287)
10-23 03:39:25.282: I/dalvikvm(1011):   at java.util.concurrent.FutureTask.run(FutureTask.java:234)
10-23 03:39:25.282: I/dalvikvm(1011):   at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
10-23 03:39:25.282: I/dalvikvm(1011):   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
10-23 03:39:25.282: I/dalvikvm(1011):   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
10-23 03:39:25.282: I/dalvikvm(1011):   at java.lang.Thread.run(Thread.java:841)
10-23 03:39:25.282: W/dalvikvm(1011): threadid=11: thread exiting with uncaught exception (group=0x41465700)
10-23 03:39:25.292: I/Choreographer(1011): Skipped 30 frames!  The application may be doing too much work on its main thread.
10-23 03:39:25.491: E/AndroidRuntime(1011): FATAL EXCEPTION: AsyncTask #1
10-23 03:39:25.491: E/AndroidRuntime(1011): java.lang.RuntimeException: An error occured while executing doInBackground()
10-23 03:39:25.491: E/AndroidRuntime(1011):     at android.os.AsyncTask$3.done(AsyncTask.java:299) 
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.util.concurrent.FutureTask.run(FutureTask.java:239)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.lang.Thread.run(Thread.java:841)
10-23 03:39:25.491: E/AndroidRuntime(1011): Caused by: java.lang.OutOfMemoryError
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.lang.String.<init>(String.java:422)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.lang.AbstractStringBuilder.toString(AbstractStringBuilder.java:642)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.lang.StringBuilder.toString(StringBuilder.java:663)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at com.dzo.redacted.driverapp.datalayer.ServerConnect.sendPostRequest(ServerConnect.java:128)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at com.dzo.redacted.driverapp.datalayer.ServerConnect.Sync(ServerConnect.java:61)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at com.dzo.redacted.driverapp.datalayer.DBSync.Login(DBSync.java:59)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at com.dzo.redacted.driverapp.asynctask.SyncDBTask.doInBackground(SyncDBTask.java:52)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at com.dzo.redacted.driverapp.asynctask.SyncDBTask.doInBackground(SyncDBTask.java:1)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at android.os.AsyncTask$2.call(AsyncTask.java:287)
10-23 03:39:25.491: E/AndroidRuntime(1011):     at java.util.concurrent.FutureTask.run(FutureTask.java:234)
10-23 03:39:25.491: E/AndroidRuntime(1011):     ... 4 more
10-23 03:39:28.091: E/WindowManager(1011): Activity com.dzo.redacted.driverapp.LoginActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{4178fcb0 V.E..... R.....ID 0,0-479,96} that was originally added here
10-23 03:39:28.091: E/WindowManager(1011): android.view.WindowLeaked: Activity com.dzo.redacted.driverapp.LoginActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{4178fcb0 V.E..... R.....ID 0,0-479,96} that was originally added here
10-23 03:39:28.091: E/WindowManager(1011):  at android.view.ViewRootImpl.<init>(ViewRootImpl.java:345)
10-23 03:39:28.091: E/WindowManager(1011):  at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:239)
10-23 03:39:28.091: E/WindowManager(1011):  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
10-23 03:39:28.091: E/WindowManager(1011):  at android.app.Dialog.show(Dialog.java:281)
10-23 03:39:28.091: E/WindowManager(1011):  at com.dzo.redacted.driverapp.asynctask.SyncDBTask.onPreExecute(SyncDBTask.java:43)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:586)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.AsyncTask.execute(AsyncTask.java:534)
10-23 03:39:28.091: E/WindowManager(1011):  at com.dzo.redacted.driverapp.asynctask.LoginAsyncTask.onPostExecute(LoginAsyncTask.java:87)
10-23 03:39:28.091: E/WindowManager(1011):  at com.dzo.redacted.driverapp.asynctask.LoginAsyncTask.onPostExecute(LoginAsyncTask.java:1)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.AsyncTask.finish(AsyncTask.java:631)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.AsyncTask.access$600(AsyncTask.java:177)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.Handler.dispatchMessage(Handler.java:99)
10-23 03:39:28.091: E/WindowManager(1011):  at android.os.Looper.loop(Looper.java:137)
10-23 03:39:28.091: E/WindowManager(1011):  at android.app.ActivityThread.main(ActivityThread.java:5103)
10-23 03:39:28.091: E/WindowManager(1011):  at java.lang.reflect.Method.invokeNative(Native Method)
10-23 03:39:28.091: E/WindowManager(1011):  at java.lang.reflect.Method.invoke(Method.java:525)
10-23 03:39:28.091: E/WindowManager(1011):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
10-23 03:39:28.091: E/WindowManager(1011):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
10-23 03:39:28.091: E/WindowManager(1011):  at dalvik.system.NativeStart.main(Native Method)
10-23 03:44:25.761: I/Process(1011): Sending signal. PID: 1011 SIG: 9

更新 1

我再次尝试with this approach,在我的情况下,toString() 也出现错误,toString() 也出现错误。当我尝试使用Amit's answer 时也发生了同样的情况。 toString() 有什么用吗?

更新 2

从 while 循环中删除了新字符串构建器对象的创建,现在我没有使用行分隔符。但是,并没有太大的不同。该应用程序首次运行。但是,如果我尝试连续运行 2-3 次,同样的问题会再次出现。

【问题讨论】:

  • 也许 [this][1] 可以解决您的问题。 [1]:stackoverflow.com/questions/8359693/…
  • 谢谢,但是该应用程序可以在手机和平​​板电脑上运行,所以我无法增加手机的堆大小

标签: android out-of-memory stringbuilder httpconnection


【解决方案1】:

这样试试,

private String sendPostRequest(String url, List<NameValuePair> params)
        throws Exception {
    String ret = null;
    BufferedReader bufferedReader = null;
    HttpClient httpClient = new DefaultHttpClient();
    HttpPost request = new HttpPost(url);

    try {
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params);
        request.setEntity(entity);

        HttpResponse response = httpClient.execute(request);
        bufferedReader = new BufferedReader(new InputStreamReader(response
                .getEntity().getContent()));
        String line = "";
        while ((line = bufferedReader.readLine()) != null) {
           ret =ret +line;
        }
        bufferedReader.close();

    } catch (ClientProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return ret;
}

【讨论】:

  • 没有工作...由于同样的原因仍然出现 OutOfMemoryError
  • 你能解释一下在上述方法中发现的错误吗?
  • @Nitish StringBuilder 仍然存在,但隐藏在字符串 + 运算符中。
【解决方案2】:

如果你没有足够的内存来存储你的字符串,你可以:释放一些内存,减少内存消耗,不要在内存中存储字符串。

根据您的情况,您需要:

  • 存储响应的内存(大约 1.5+mb)
  • 字符串生成器的内存 (大约 1.5+mb)
  • 结果字符串的内存(大约 1.5+mb 连续内存,非常糟糕)
  • BufferedReader/InputStreamReader...等。 (几公斤)

选项:

  1. 将响应转换为字符串without string builder。它将节省 1+mb。
  2. 以流的形式读取响应并将其转换为不带字符串生成器的字符串。 Request/Response entity streaming。它将节省大约 3mb
  3. 不要将响应转换为字符串。将其作为流读取并保存到文件或数据库中,或者将流提供给流式 JSON 解析器。
  4. 在发送您的发帖请求之前尽可能多地释放内存,并开始祈祷一切都会好起来。

【讨论】:

  • 感谢您指出错误。我选择了第三种解决方案。它工作正常。
  • 先生,这是否意味着如果我将输入流直接传递给 gson 本身,那么我不会遇到使用字符串生成器时发生的 OOM 异常?请协助。谢谢
  • 我不明白具有 512MB RAM 和大量空闲空间的设备如何难以为字符串分配微不足道的 6MB 或 8MB。
  • 嗨 Leonidos / Nitish,能否给我第三个选项的参考链接?
【解决方案3】:

只需将其添加到清单中的 &lt;application /&gt; 标记即可:

android:largeHeap="true"

【讨论】:

  • 这不是修复,它只是隐藏了问题。
猜你喜欢
  • 1970-01-01
  • 2013-01-17
  • 2012-03-07
  • 1970-01-01
  • 2014-12-30
  • 2011-11-25
  • 2013-01-17
  • 1970-01-01
相关资源
最近更新 更多