72

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
  • 21,220
  • 37
  • 123
  • 171
  • 6
    Note that your first assumption is incorrect: the Activity "A" will never be removed from the memory. See http://commonsware.com/blog/2011/10/03/activities-not-destroyed-to-free-heap-space.html – Thierry Roy Mar 01 '12 at 18:13
  • 1
    @Thierry-DimitriRoy, unbelievable... I feel like I have been living a lie... I almost don't believe it... – cottonBallPaws Mar 04 '12 at 05:40
  • System doesn't kill a single activity or fragment. It can only kill the entire process to reclaim memory when it runs low. – stdout Apr 02 '19 at 13:22

4 Answers4

105

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.

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.

starball
  • 20,030
  • 7
  • 43
  • 238
Ivan Bartsov
  • 19,664
  • 7
  • 61
  • 59
  • 30
    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. – hackbod Feb 11 '12 at 06:16
  • 1
    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. – Ivan Bartsov Feb 12 '12 at 08:11
  • 1
    @hackbod What do you mean by `major allocations`? – theblang May 08 '14 at 15:39
  • @mattblang If I may, most likely Dianne meant anything memory-heavy and not managed by fragment's view hierarchy, most notable example being `Bitmap`s. If your fragment holds on to a couple of primitives or average-sised `String`s -- that's probably OK. But dispose of anything several instances of which would get you over the process memory cap. – Ivan Bartsov May 08 '14 at 19:02
  • @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? – traninho Nov 05 '14 at 14:17
  • 3
    @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 the`Activity` 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. – Ivan Bartsov Nov 07 '14 at 10:19
  • @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(). – traninho Nov 07 '14 at 10:33
  • @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. – Ivan Bartsov Dec 15 '14 at 07:02
  • @IvanBartsov No, It is not necessary, I was only curious. But thank you anyway! :) – traninho Jan 05 '15 at 14:48
  • Best answer, contratz! – Natan Lotério Oct 01 '15 at 19:06
  • @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. – stdout May 26 '17 at 09:55
7

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.

Beowulf Bjornson
  • 1,626
  • 1
  • 14
  • 24
4

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.

Bill Gary
  • 2,987
  • 2
  • 15
  • 19
  • 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... – cottonBallPaws Dec 13 '11 at 00:19
  • @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. – Warpzit Feb 08 '12 at 08:51
1

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.

Sid
  • 7,511
  • 2
  • 28
  • 41