Mxyk
Mxyk

Reputation: 10698

Efficient Loop to Avoid Multiple Garbage Collect Calls for Android

I have an Android app that allows the user to record data (such as accelerometer, latitude, longitude, etc.). There are a total of 9 of these fields, and the user can record for up to 10 minutes (3000 records per field). Therefore, a total of 27,000 data points can be collected. The user can also take pictures and videos to upload the SD Card as well.

When the user is done collecting data (or the 10 minutes are up), the data is stored in a String, which is later uploaded to the SD Card as a .csv file. The problem though is that it takes forever for the data to append to the string, due to the massive amount of garbage collecting (it appears to be approximately 5 collects or so per second!) The appending starts off fast, but seems to be slowing down as more and more as data is added.

This is the loop that causes the lag:

           for( i = 0 ; i < len2 ; i++ ) {
                data += accelX[i] + ", " + accelY[i] + ", " + accelZ[i] + 
                ", " + accelT[i] + ", " + latitu[i] + ", " + 
                longit[i] + ", " + orient[i] + ", " + 
                magneX[i] + ", " + magneY[i] + ", " + magneZ[i] + 
                ", " + millis[i] + "\n";

                partialProg = 100.0 * ( (double)(i+1) / (double)(len2));
                dia.setProgress((int) partialProg);

            }

data is just a String, and nothing is being newed, so I'm unsure of why GC is being called so often. My question is: what is the problem here, and/or how can I make this more efficient?

Upvotes: 2

Views: 439

Answers (4)

seanhodges
seanhodges

Reputation: 17524

You could use StringBuilder to concatenate the data:

StringBuilder sb = new StringBuilder();
for( i = 0 ; i < len2 ; i++ ) {
    sb.append(accelX[i]).append(", ");
    sb.append(accelY[i]).append(", ");
    sb.append(accelZ[i]).append(", ");
    sb.append(accelT[i]).append(", ");
    sb.append(latitu[i]).append(", ");
    sb.append(longit[i]).append(", ");
    sb.append(orient[i]).append(", ");
    sb.append(magneX[i]).append(", ");
    sb.append(magneY[i]).append(", ");
    sb.append(magneZ[i]).append(", ");
    sb.append(millis[i]).append("\n");
}

StringBuilder is inherently faster for building long strings.

It also avoids allocating so many String objects into the heap; since each += operator is creating a new String object rather than modifying the last one. This in turn leads to a large number of GC calls to clean up all the redundent String objects. See also: http://chaoticjava.com/posts/stringbuilder-vs-string/

As Marcelo points out; you may also find dealing with large amounts of data in memory may become problematic on low-spec Android devices, at which point you should consider appending the contents of your StringBuilder to a temporary file every X number of iterations to keep the footprint low. At the end of the process you can stream the file to whatever the destination is planned to be, or read segments of it back to memory on demand.

Upvotes: 1

RokL
RokL

Reputation: 2812

+ operator on strings actually uses StringBuilder, thus you do perform at least two allocations per loop (new StringBuilder and then StringBuilder creates a string when .toString() is called on it to assign the result to data). Best way to tackle this is to create a StringBuilder in front of the loop.

StringBuilder buf = new StringBuilder();
String sep = ", ";
for( i = 0 ; i < len2 ; i++ ) {
    buf.append(accelX[i]).append(sep).append(accelY[i]).append(sep);
    buf.append(accelZ[i]).append(sep).append(accelT[i]).append(sep);
    buf.append(latitu[i]).append(sep).append(longit[i]).append(sep);
    buf.append(magneX[i]).append(sep).append(magneY[i]).append(sep);
    buf.append(magneZ[i]).append(sep).append(millis[i]).append('\n');
    partialProg = 100.0 * ( (double)(i+1) / (double)(len2));
    dia.setProgress((int) partialProg);
}

Upvotes: 1

kosa
kosa

Reputation: 66637

One improvement you may do is, StringBuffer to construct data, that way you don't you can avoid String construction and concatenation operations. For example:

StringBuffer buff = new StringBuffer();
buff.append(accelX[i]).append(...).apend(...)

Upvotes: 1

amit
amit

Reputation: 178431

You are creating a lot of objects. every time you use the operator+, you actually create a new object, which is expansive [if you repeat it a lot of times]

You can make it more efficient by using a StringBuilder and append to it, and create the String when you are done.

for example:

sb.append(accelX[i]).append(',').append(accelY[i]).append(',').append(accelZ[i]);

[where sb is an instance of StringBuilder]

Upvotes: 3

Related Questions