Question

Spent 3 days on this issue and I'm stumped, could reallly use your help on this.

My app uses a single activity that contains a fragment manager. In its current state, I've designed it to only have a single fragment attached at any one time. I call:

getSupportFragmentManager().beginTransaction().replace(R.id.main_fragment, mCurrent).commit();

to remove the existing fragment and replace it with the new one. The fragments are not being stored anywhere else. (Just mCurrent and the transaction manager which get replaced when the next fragment is opened)

The issue that occurs is not an OutOfMemory exception, but instead the entire app restarts because the device itself runs out of memory. The logs says nothing except stuff like DeadObjectException and:

9-17 11:34:14.742: W/InputDispatcher(341): channel ~ Consumer closed input channel or an error occurred.  events=0x9

09-17 11:34:14.742: E/InputDispatcher(341): channel ~ Channel is unrecoverably broken and will be disposed!

These are coming from the device logs and not my app logs. And they don't provide enough information for me to trace what is causing them. The memory from the app is never released and the tablet becomes unusable from that point.

I've used all the tools I know available to analyze what the memory leak would be but nothing stands out. I used MAT (MemoryAnalyzerTool) and the Eclipse DDMS tools to figure out what may be going wrong. MAT reports between 8MB-16MB of total memory in use after I visit many different fragments. I look at the Leaks Report and nothing seems out of the ordinary. About 3MB for Imageviews, 3 MB for Bitmaps, and 8MB for other. In DDMS, the heap says I'm using ~50% of the app's allotted size. When I look at the Settings at the running processes, each Fragment I visit makes the memory amount from my app fluctuate between 30-140MB however the device itself never decreases in memory, hence why the device runs out of memory. Even when the app is completely closed, exited, destroyed, the memory is never released. All 759MB are used even though the sum of all the current running apps is about 200-300MB.

My assumption is that it is either holding on to the fragment itself in memory, or something contained within these fragments even though my app isn't. I call GC after each fragment swap.

I'm using the Samsung Galaxy Tab 2 10.1", but the same issue occurs on Motorola Xoom.

Questions:

  1. Has anyone experienced this kind of behavior?
  2. Can anyone provide me any direction as to other tools to help me research a possible memory leak?
  3. Is there a system process that I can look at to show me in detail how the device memory is being allocated?

Thanks for your time.

Was it helpful?

Solution 3

I found the primary cause of my memory leak. It was around executing a shell command by creating a Process object and opening up input and output streams. I used this to detect rooted devices. I'm not enough of an expert on the Process class to explain why this is causing leaks (I'm going to read up on it now), but if you're using code similar to this I'd be alert to this issue.

public static enum SHELL_COMMAND
{
    check_su_binary(new String[] { "/system/xbin/which", "su" }), ;
    String[] command;

    SHELL_COMMAND(String[] command)
    {
        this.command = command;
    }
}

/**
 * @param shellCommand
 * @return
 */
public List<String> executeCommand(SHELL_COMMAND shellCommand)
{
    //this code was causing my memory leak
    try
    {
        String line = null;
        List<String> fullResponse = new ArrayList<String>();
        Process localProcess = Runtime.getRuntime().exec(shellCommand.command);

        OutputStreamWriter writer = new OutputStreamWriter(localProcess.getOutputStream());
        BufferedWriter bufferedWriter = new BufferedWriter(writer);

        InputStreamReader reader = new InputStreamReader(localProcess.getInputStream());
        BufferedReader in = new BufferedReader(reader);

        while ((line = in.readLine()) != null)
        {
            fullResponse.add(line);
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    try
    {
        bufferedWriter.close();
        bufferedWriter = null;
        writer.close();
        writer = null;

        in.close();
        in = null;
        reader.close();
        reader = null;
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    return fullResponse;
}

OTHER TIPS

Memory leaks in native code can create the symptoms that you're describing: system memory slowly bleeds away while the app's memory footprint (as seen by the JVM) stays more or less constant.

I suggest looking carefully through the memory allocations in your native code and verifying that each one is being cleaned up. This includes calls to malloc, any objects created on the heap (i.e., with "new"), and so forth. There is some tool support for profiling native allocations with DDMS -- see this answer.

Upon further analysis, what I can deduce is that there were three main problems of my memory leaks:

  1. I had initially placed all the UI creation code in the Overridden onStart method because I was unclear of the appropriate location for it (onStart vs onCreateView). I moved all UI code into onCreateView (anything that is related to this.getView() -> use your inflater in the onCreateView). This seemed to eliminate a memory leak that occurred every time I opened and closed the app continuously. Here's a recommendation:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View v = inflater.inflate(R.layout.fragment_one, container, false);
        //ALL UI code here using "v"
        return v;
    }
    
  2. I was using a ViewPager to house all my fragments (this was because of the specific type of navigation I needed). I switched this to using a custom fragment management solution. I would recommend the use of fragment transaction manager (I originally thought that the fragment manager was causing the leak, but it doesn't appear to be). This wasn't causing a leak, but was eating up a significant amount of memory.

  3. WebViews are a significant cause of memory leaks as well (as its been well documented throughout the internet). I tried using a solution posted which was to wrap the webview in a container and destroy both the webview and its container (Here's the code)

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if (mWebContainer != null)
            mWebContainer.removeAllViews();
        if (mWebView != null)
        {
            mWebView.loadUrl("about:blank");
            mWebView.destroy();
            mWebView = null;
        }
    }
    

This did not fix my webview issue, but maybe it will fix someone else's out there. I'm continuing to search for the problem. My scenario includes multiple fragments that each have a webview that can open up additional fragments with webviews.

I hope this helps others, I was stuck for over a month debugging all the different scenarios causing leaks as I didn't consider originally that there were many causes. Good luck. Thanks SO community as always.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top