I’ve been noticing in our Android app that every time we exit to the home screen we increase the heap size (leak) by the amount of the ByteArrayOutputStream. The best I have been able to manage is by adding
this.mByteArrayOutputStream = null;
at the end of run() to prevent the heap size increasing constantly. If anyone could enlighten me I would be very appreciative. I wrote out the following example that illustrates the problem.
MainActivity.java
public class MainActivity extends Activity {
private Controller mController;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
protected void onStart() {
super.onStart();
this.mController = new Controller();
mController.connect();
}
@Override
protected void onStop() {
super.onStop();
mController.quit();
}
}
Controller.java
public class Controller {
public volatile ReaderThread mThread;
public Controller() {
super();
}
public void connect() {
mThread = new ReaderThread("ReaderThread");
mThread.start();
}
public void quit() {
mThread.quit();
}
public static class ReaderThread extends Thread {
private volatile boolean isProcessing;
private ByteArrayOutputStream mByteArrayOutputStream;
public ReaderThread(String threadName) {
super(threadName);
}
@Override
public void run() {
this.isProcessing = true;
Log.d(getClass().getCanonicalName(), "START");
this.mByteArrayOutputStream = new ByteArrayOutputStream(2048000);
int i = 0;
while (isProcessing) {
Log.d(getClass().getCanonicalName(), "Iteration: " + i++);
mByteArrayOutputStream.write(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
try {
mByteArrayOutputStream.reset();
mByteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Log.d(getClass().getCanonicalName(), "STOP");
}
public void quit() {
this.isProcessing = false;
}
}
}
Threads are immune to GC because they’re garbage collection roots. So, it’s likely that the JVM is keeping your
ReaderThreadin memory, along with its allocations for member variables, thus creating the leak.Nulling out the
ByteArrayOutputStream, as you’ve noted, would make its buffered data (but not theReaderThreaditself) available for GC.EDIT:
After some sleuthing, we learned that the Android debugger was causing the perceived leak: