0

Here is my very simple code :

class UpdateAlertActivity extends Activity { O o ; WeakReference<O> _o ; ArrayList<O> arr = new ArrayList<>(); static class O { private String l ; public O(String l) { this.l = l ; } } void test() { arr.clear(); Runtime.getRuntime().gc(); Log.i(LOG_TAG, "breakpoint"); } @Override protected void onResume() { super.onResume(); o = new O("hello"); arr.add(o); _o = new WeakReference<>(o); test(); } } 

As I clear array in test() method, I was excepting _o.get() to be null at my breakpoint, but it's not the case. When I see in the debbuger where o is held, it shows me only in the weak reference _o, while I thought weak reference are made to free their instance when there is no other strong reference on them...

I saw on StackOverflow that I missed calling GC, but calling it had no effect in my program as you can see.

EDIT : Even this super simple code does not work as excpected, it makes no sense...

O o = new O("lol"); WeakReference<O> _o = new WeakReference<>(o); Log.i(LOG_TAG, "1:" + _o.get().toString()); o = null ; Log.i(LOG_TAG, "2:" + _o.get().toString()); System.gc(); Log.i(LOG_TAG, "3:" + _o.get().toString()); // output not null here 

EDIT2 : I wanted the use of GC because I have, in another static class, an array called lastUpdates, which is filled with app updates sent by my server wia TCP, sometimes.

All my activities observes this lastUpdate array (by implementing Observer/Observable interface), and when it is changed, all are notified, and call a particular function onUpdate(ArrayList u) with the list of new arrived updates as a parameter. This function process the updates only if the activity is resumed (changing UI during activity sleep causes app to crash).

If the entiere app is "sleeping" (user is in device menu for example), I want only the first activity waking up to be able to process new updates.

For that, I created in my activity class a array pendingPersistentUpdates storing weak references of all updates that has been catch when activity was sleeping. When the first activity wakes up, the updates catch during app sleep time are still in lastUpdates array, so I expect the weak references stored in pendingPersistentUpdates activity prop to return the actual updates, so my activity can UI-process them, at onResume.

I was expecting this situation :

  • An activity A run, an activity B is paused (behind activity A for example)
  • App receives an update U. All activities (A and B) are notified. A process the update because it's running, as expected. B store this update in its pendingPersistentUpdates, because it's paused.
  • A pause, user returns to B. At A pause, lastUpdates array is cleared, so the weak references of the updates in B pendingPersistentUpdates would return null
  • B resume, but pendingPersistentUpdates weak references are null (lastUpdates has been cleared), so U is not processed.

Though, as lastUpdate.clear() does not fire GC, B pendingPersistentUpdates updates still exist, and are processed a second time (behavior not desired)

3
  • Runtime.getRuntime().gc() may not always run the garbage collector Commented Oct 15, 2020 at 12:06
  • I also tried with System.gc() but it has no effect either.. Commented Oct 15, 2020 at 12:14
  • @JeremLachkar both answers already posted when you last edited your question also explain what you're observing with your newly added paste. Commented Oct 15, 2020 at 12:28

2 Answers 2

1

It is not possible to force the garbage collector to run. Runtime.getRuntime().gc() is just an alias for System.gc(), and both are just a hint (read the docs; they mention this). In particular, calling gc() usually means the collection will occur in some other thread; so it's more the 'start sign' for the collector; gc() doesn't necessarily pause and wait around for the gc to finish a full cycle, it merely tells the gc to start one. Whilst, again, no guarantee, Thread.sleep(10000L); makes it more likely you'll see the effects of the GC call.

A WeakReference merely guarantees that the existence of this object won't impede on any garbage collection of the object it refers to. That's all. It doesn't instantaneously lose its referent when the only way to get to the referent is via WeakReference objects. That'll happen later (in fact, that will happen before the object is GCed: First the collector will wipe out all weakrefs, and sometime later will the memory that the object occupied be truly free for use). So, what you're observing in your debugger (that the only way to get to the object is via that WR) isn't inherently broken.

Note also that the debugger itself can be the issue. Merely being there and observing it can have an effect (so make sure you run it without as well).

If you want the GC to try harder, you can allocate some fairly large arrays and pause the thread, that can help, but there is nothing you can do to guarantee a GC run. If you want to observe, run java with java -verbose:gc restOfArgsHere, and keep an eye on the console; if a GC actually occurs, you'll see it.

However.

Your code as pasted doesn't even compile which suggests you either pasted only half of it, or you edited it some 'for clarity' (often a bad idea, best to paste precisely what you're working with!). In particular, you write o = new O("hello");, but o isn't defined anywhere. If it is a field of your class, it would imply the referent cannot be collected at all! So you might want to check on that. Make that O o = new O("hello"); instead of what you have. Assuming you read your debugger correctly and it's functioning properly, this isn't it, but it is suspicious.

Sign up to request clarification or add additional context in comments.

3 Comments

Ah, android. That makes -verbose:gc a lot trickier.
Thanks for your answer ! Sorry you're right I forgot a line a the code (I added it), o is a property of the class. What do you mean by "If it is a field of your class, it would imply the referent cannot be collected at all" ? I will edit the question to clarify the context.
With the code as you wrote it, that object cannot be collected because the main thread refers to UpdateAlertActivity, and it refers to that object via the field o - you'd have to explicitly null it out, in addition to the rules about how System.gc() doesn't just cause garbage to be collected inline.
1

Calling the garbage collector only suggests to the system that it should free up memory, it does not force it explicitly. This means that the collection may not actually happen if there is enough memory.

https://developer.android.com/reference/java/lang/System#gc()

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.

Notice the next sentence "the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects"

Internally the garbage collector uses heuristics / thresholds to decide when to collect unused objects, so, basically when the JVM needs memory.

1 Comment

Thanks for your answer. I didn't know it didn't execute immediatly the GC. My program needs the garbage collector to be run to work properly. I will clarify the context

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.