When a Fragment is replaced and put in the back stack (or removed) does it stay in memory?

40,359

Solution 1

Take a look at this: BackStackRecord.Op.fragment

That is how fragments are stored in the back stack. Note the live reference, neither WeakReference nor SoftReference are used there.

Now this: FragmentManagerImpl.mBackStack

That is where manager stores the back stack. Simple ArrayList, also, no WRs or SRs.

And finally this: Activity.mFragments

That is the reference to the fragment manager.

GC can only collect objects that have no live references (are not reachable from any thread). That means, until your Activity is destroyed (and so, FragmentManager reference is gone), GC will not be able to collect any of the Fragments in the back stack.

Note that when Activity is destroyed and retains state (like when you turn the device to landscape mode), it doesn't retain actual Fragment objects in the stack, only their states - Fragment.FragmentState objects, i.e. actual fragments in the back stack are re-created every time activity gets re-created with retained state.

Hope this helps.

PS So, in short: Yes, you can run out of memory by adding Fragments to back stack as well as by adding too many views to view hierarchy.

UPD Considering your example, F will stay in memory until C is killed. If C is killed and then resurrected with different configuration - F will be destroyed and reincarnated in a different object as well. So, F's memory footprint is around until C loses state or back stack is cleared.

Solution 2

I'm sorry for not being able to provide you with some official source of information, but I was curious too to see what would happen and decided to test it. And according to my tests, yes, you run the risk of running out of memory.

I had to add an incredible amount of Fragments (more than one hundred) in a for loop for the OutOfMemoryError to happen, but it did happen. And checking my logs, I could see that the onCreate() and onCreateView() methods were called a lot of times but onSaveInstance(), onPause() and onDestroy weren't called at all.

For reference, this is how I added the fragments to the backstack:

getSupportFragmentManager().beginTransaction().add(R.id.scene_fragment_container, mSceneFragment).addToBackStack("FOOBAR").commit();

And the Fragments I added were somewhat simple: an ImageView, an EditText, a couple TextViews, a SeekBar and a ListView.

But unless you are holding a huge amount of data in memory it shouldn't be an issue.

Later I tried adding only 50 to the backstack, killing the app and restarting it. And as I hoped/guessed, all the fragments were restored (and the onSaveInstance() and onPause() methods called) so my lifecycle implementation wasn't the issue that caused the OutOfMemoryError to fire.

Solution 3

From developer.android.com/guide/topics/fundamentals/fragments.html

A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments. However, while an activity is running (it is in the resumed lifecycle state), you can manipulate each fragment independently, such as add or remove them. When you perform such a fragment transaction, you can also add it to a back stack that's managed by the activity—each back stack entry in the activity is a record of the fragment transaction that occurred. The back stack allows the user to reverse a fragment transaction (navigate backwards), by pressing the BACK button.

Solution 4

Yes you can run out of memory creating too many fragments in a single activity. The fragments will only be destroyed when the containing Activity is.

Share:
40,359
cottonBallPaws
Author by

cottonBallPaws

Updated on July 05, 2022

Comments

  • cottonBallPaws
    cottonBallPaws almost 2 years

    Is the behavior similar to the way Activities work? For example with Activities it works like this:

    Activity A starts Activity B, while B is on screen, the system is able to remove A from memory if it is needed by the system. Upon pressing BACK, A will be recreated into memory as if it never left in the first place.

    I have looked for a clear explanation of what happens memory wise with Fragments and haven't found anything. Does it work the same way? For example:

    Activity C has Fragment F in its layout. Then, at some point F is replaced by Fragment G, but F is kept in its back stack.

    Will F stay in memory until the C is killed or can it be removed by the system as needed?

    Really what I am asking is whether or not I run the risk of running out of memory if I have a back stack of complicated Fragments in a single Activity?

  • cottonBallPaws
    cottonBallPaws over 12 years
    Thanks Bill. However, that still doesn't say one way or another whether the Fragments in the activities' back stack are retained in memory or released as needed, and that is what I'm wondering...
  • Warpzit
    Warpzit about 12 years
    @littleFluffyKitty why doesn't this answer your question? Obviously they are in memory, but paused just like a regular activity, they are not cleaned up until you go back or your activity is destroyed.
  • hackbod
    hackbod about 12 years
    Note however that when a fragment is put on the back stack, its onDestroyView() is called. If at that point you clean up any major allocations being held by the fragment, you shouldn't have memory issues.
  • Ivan Bartsov
    Ivan Bartsov about 12 years
    Yes, solid point. Fragment objects will still take up some amount of mem and strictly speaking one still can flood memory with a huge amount of fragments even if the clean up logic is in place, but you're definitely right - if cleaned up properly, in real conditions (i.e. when amount of fragments is sensible) back stack fragments shouldn't cause any considerable memory overhead.
  • theblang
    theblang almost 10 years
    @hackbod What do you mean by major allocations?
  • Ivan Bartsov
    Ivan Bartsov almost 10 years
    @mattblang If I may, most likely Dianne meant anything memory-heavy and not managed by fragment's view hierarchy, most notable example being Bitmaps. If your fragment holds on to a couple of primitives or average-sised Strings -- that's probably OK. But dispose of anything several instances of which would get you over the process memory cap.
  • traninho
    traninho over 9 years
    @hackbod Is it also necessary to null fragment's View references as well? I mean, if fragment is destroyed (onDestroyView() is called) and GC wants to collect garbage, will the old fragment's Views be collected if the references to the views stay in the fragment (memory leak)? Or it does not matter because if fragment is destroyed then all objects holt by fragment (only by fragment) can be collected by GC?
  • Ivan Bartsov
    Ivan Bartsov over 9 years
    @traninho Good notion. When put to back stack, a Fragment instance is not destoyed, so onDestroyView() should always null any view references it has. Otherwise it will keep the dead views from getting GCed. Also, non-backstack retained fragments (see setRetainInstance(true)) are not destroyed even when theActivity is recreated, so by keeping view references you can leak a whole Activity. Normally this doesn't happen though, because in 99% of the cases view refs are updated in onCreateView() which does get called on Actvity recreation, so after that refs point to new views anyway.
  • traninho
    traninho over 9 years
    @IvanBartsov Thank you for the response. Currently I am struggling with OOM in my app. For simplicity I have 1 activity (A) and one fragment (F) which holds viewpager with 2 another fragments (F1 and F2). From F1, if user clicks on a button, then another instance of (F) is opened from the A and added to a backstack. So if user does this several times, then OOM will occur sooner or later. I will try to null every view reference in onDestroy().
  • Ivan Bartsov
    Ivan Bartsov over 9 years
    @traninho Well, stuff like this can't be easily diagnosed without code. We should take this to a chat room if you want more help.
  • traninho
    traninho over 9 years
    @IvanBartsov No, It is not necessary, I was only curious. But thank you anyway! :)
  • Natan Lotério
    Natan Lotério over 8 years
    Best answer, contratz!
  • stdout
    stdout almost 7 years
    @IvanBartsov I guess I'm missing one point here. FragmentState object is the one that is only retained across configuration changes, not the corresponding actual fragment. But FragmentState object contains a reference (mInstance) to it's fragment and it's a strong one. So, how GC manages to destroy fragment without destroying it's state object which includes a reference to it's fragment.