Android: Out of Memory error StringBuilder
Solution 1
If you do not have enough memory to store your string you can: free some memory, reduce memory consumtion and do not store string in memory.
In your case you need:
- memory to store response (around 1.5+mb)
- memory for string builder (around 1.5+mb)
- memory for resulting string (around 1.5+mb continious memory, very bad)
- BufferedReader/InputStreamReader...etc. (some kilos)
Options:
- Convert response to a String without string builder. It will save 1+mb.
- Read response as a stream and convert it to a String without string builder. Request/Response entity streaming. It will save around 3mb
- Do not convert response to a string. Read it as stream and save to a file or db, or give a stream to streaming JSON parser.
- Free as much memory as you can before sending your post request and start praying that everything will be alright.
Solution 2
Just add this to the <application />
tag in your manifest:
android:largeHeap="true"
Related videos on Youtube
Nitish
Updated on September 16, 2022Comments
-
Nitish over 1 year
In my app, I fetching data from the server in the form of JSON. The data is around 1.5 MB. The app works but sometimes it crashes while fetching data from server giving OutOfMemoryError.
This is my method:
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; }
I tried setting the StringBuilder to null after use but didn't helped. I am posting the logcat traces below:
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
Update 1
I tried with this approach again it gave error with toString()in my case also, it is giving error with toString(). The same happened while I tried with Amit's answer. Is there anything with toString().
Update 2
Removed creation of new String builder object from while loop and now I am not using line separator. But, it didn't made much difference. The app runs for first time. But, if I try to run continuously for 2-3 times, the same problem occurs again.
-
Nitish over 10 yearsThanks but, the app will run on phones and tablets, so I cannot increase heap size of a phone
-
-
Nitish over 10 yearsdidn't worked...Still getting OutOfMemoryError for the same reason
-
Nitish over 10 yearsCan you shed some light on finding wrong in the above approach.
-
Nitish over 10 yearsThanx for pointing out the bug. I had opted for 3rd solution. It works fine.
-
Rat-a-tat-a-tat Ratatouille over 10 yearsSir, does that mean if i pass an inputstream to gson itself directly, then i would not face the OOM exception that takes places when using a string builder? Please assist. Thanks
-
Michael over 9 yearsI do not understand how a device with 512MB RAM and plenty free can have trouble allocating a paltry 6 or 8MB for a string.
-
Michael over 9 years@Nitish The StringBuilder is still there but is being hidden in a string
+
operator. -
Naveen Kumar M over 8 yearsHi Leonidos / Nitish, Can you please give me reference link for 3rd option?
-
Omid Heshmatinia almost 4 yearsIt is not a fix, it just hides the problem.