I am working on an application that needs to be compatible with Android 2.3 (Gingerbread), and the device I'm using for development tests is a Motorola Atrix MB860 running Android 2.3.6.
In this device I get roughly 40MB of maximum heap space and as far as I could realize, my app uses around 33MB, but I get an OutOfMemoryError
exception anyway.
Basically, the part of my code that matters to this issue creates a large String
(8MB - I know it's rather big, but if it's too small it won't satisfy one of the requirements) and then goes on to create 2 threads that use such string to write to a certain memory space concurrently.
Here is the code:
// Create random string
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(8388608); // ensuring 8 MB is allocated on the heap for the StringBuilder object
for (long i = 0; i < DATA_SIZE; i++) {
char c = chars[new Random().nextInt(chars.length)];
sb.append(c);
}
String randomByteString = sb.toString();
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
Runnable worker = new SlidingBubbles(param1, param2, randomByteString)
executor.execute(worker);
}
// This will make the executor accept no new threads
// and finish all existing threads in the queue
executor.shutdown();
// Wait until all threads are finish
while(!executor.isTerminated()) {
// wait for bubble threads to finish working...
}
and the threads' routine:
private class SlidingBubbles implements Runnable {
private int param1, param2;
private String randomByteString;
private final Object mSignal = new Object();
private volatile long tempBytesWritten = 0;
private volatile long totalBytesWritten = 0;
public SlidingBubbles(int param1, int param2, String randomByteString) {
this.param1= param1;
this.param2= param2;
this.randomByteString = randomByteString;
}
private void doIt() {
File file = null;
RandomAccessFile randomAccessFile = null;
FileChannel fc = null;
try {
while(param1> 0) {
// Instantiate the 1st bubble file
file = new File(TARGET_DIR, String.valueOf(Calendar.getInstance().getTimeInMillis()));
while(param2 > 0) {
randomAccessFile = new RandomAccessFile(file, "rwd");
fc = randomAccessFile.getChannel();
fc.position(fc.size());
synchronized (mSignal) {
tempBytesWritten = fc.write(ByteBuffer.wrap(randomByteString.getBytes()));
totalBytesWritten += tempBytesWritten;
}
// some other things that don't matter
}
@Override
public void run() {
wipe();
}
}
Awkwardly (to me), on the 30th line of the thread routine (tempBytesWritten = fc.write(ByteBuffer.wrap(randomByteString.getBytes()));
), the 2nd thread ("pool-1-thread-2") launches the exception, exits, and the 1st thread ("pool-1-thread-1") continues (actually, starts) executing normally.
By the time the JVM is done allocating space for that large String
, the app is using 33MB of the heap. As you can see from the code, thatString
is created only once, but then used multiple times from both threads.
Shouldn't the threads be using just a reference to the String
rather than copying it? (which would, in this case, exceed the 40MB allowance).
I must also point out that it is (or at least seems to be, as far as my understanding goes) impossible to increase this heap space on Gingerbread (previous research).
Is there anything I'm missing? Any help is greatly appreciated.