0

It seems no one has reported similar situations... I have absolutely no idea what happened...

I have a StringBuilder of size 8Mb to process a large String result. I am trying to reuse the StringBuilder. I thought setting length = 0 will just reset the counter and not allocate new memory?

try {
      //result.length() around 4Mb
        StringBuilder sBuilder = new StringBuilder(result.length());
        result = DoSomethingToResult1(sBuilder, result); //shrink result a bit using replaceAll

        try {

            sssBuilder.setLength(0);
            result = DoSomethingToResult2(sBuilder, result); //shrink result further using replaceAll
        } catch (OutOfMemoryError e) {
             Log.d(TAG, "Out of Memory on 2");
        }

        try {
            sBuilder.setLength(0);  //OutOfMemory thrown here.          
            result = DoSomethingToResult3(sBuilder, result); //shrink result even further using replaceAll
        } catch (OutOfMemoryError e) {
             Log.d(TAG, "Out of Memory on 3");
        }

    } catch (OutOfMemoryError e) {
                Log.d(TAG, "Cannot create sBuilder");
    }

The process usually dies on the second setLength(0), sometimes dies on the first setLength(0), but it can always create the sBuilder in the beginning.

In DoSomethingToResult, I split result into chunk of 100Kb size and append to sBuilder one by one, returning sBuilder.toString(). So problem does not come from replaceAll. And since it passed the first process, I don't think the toString() is the problem either.

I tried:

sBuilder.setLength(0);
System.gc();
try{
    Thread.sleep(1000);
}catch(Exception e){};

or

System.gc();
try{
    Thread.sleep(1000);
}catch(Exception e){};
sBuilder.setLength(0);

Both failed.

Log output:

E/dalvikvm-heap(27130): Out of memory on a 8723792-byte allocation.
I/dalvikvm(27130): "AsyncTask #1" prio=5 tid=11 RUNNABLE
I/dalvikvm(27130):   | group="main" sCount=0 dsCount=0 obj=0x42036a18 self=0x51c2eb08
I/dalvikvm(27130):   | sysTid=27151 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1371731208
I/dalvikvm(27130):   | schedstat=( 0 0 0 ) utm=197 stm=17 core=1
I/dalvikvm(27130):   at java.lang.AbstractStringBuilder.setLength(AbstractStringBuilder.java:~567)
I/dalvikvm(27130):   at java.lang.StringBuilder.setLength(StringBuilder.java:44)
Ivan
  • 149
  • 1
  • 12

1 Answers1

0

Setting length to 0 doesn't automatically invoke the GC to kick in. What you can do is have a loop and call System.gc, then wait for a while until available memory increases. Then break from the loop and continue with your next process.

You can also try allocating a new builder rather than clearing the buffer in each iteration.

Sajal Dutta
  • 18,272
  • 11
  • 52
  • 74
  • As an addition, there is a small discussion given [here](http://stackoverflow.com/a/5193094/1798304) about that issue. – MalaKa Mar 26 '14 at 10:02
  • @MalaKa This question looks the same as the question link you provided. – Sajal Dutta Mar 26 '14 at 10:04
  • Yes, that's why I provided a link to one of the answers with a discussion in the comments that might be interesting (and supporting your answer). What's the issue? – MalaKa Mar 26 '14 at 10:10
  • @MalaKa Was thinking if this should be marked as duplicate. – Sajal Dutta Mar 26 '14 at 10:11
  • Okay, I see. But I don't think so, since this is question is about a particular problem and the other question is more best practice. Similar, but to me not duplicate. – MalaKa Mar 26 '14 at 10:16
  • Yes I have read that link before. Problem is allocating new builder is not fast enough for the garbage collector. So once new builder is created again with 8Mb I got OutOfMemoryError. – Ivan Mar 26 '14 at 12:10
  • @Ivan Did you try what I suggested? – Sajal Dutta Mar 26 '14 at 13:01
  • I am not free to do so yet, but thanks for the suggestion. Will try later. For the moment I will just wrap up the whole thing to catch the OOME and return the unprocessed string. – Ivan Mar 26 '14 at 13:23
  • Hmm... not really working. Whenever it calls setLength(0), it kicks in to allocate a 8Mb slot and OOME, no matter where I put System.gc before or after setlength(0)... I tried waiting for 1000ms after calling System.gc – Ivan Mar 27 '14 at 15:17
  • @Ivan First do setLength to 0. Then call gc. After that wait 1 second. See if there was any change on available free space. If there was, do the allocation. – Sajal Dutta Mar 27 '14 at 16:34
  • That is what I did. Before gc kick in I got OOEM at setlength(0) – Ivan Mar 27 '14 at 18:26
  • @Ivan From the code you posted, I do not see anything special going on. Before you even do the first allocation, can you please check how much available memory you have? I think by the time you start allocating for the buffer, you are already short in memory. – Sajal Dutta Mar 28 '14 at 08:51