Implement "assert dies before" in ObjectWatcher
This is an idea from the paper LeakChaser: helping programmers narrow down causes of memory leaks
ReachabilityWatcher.expectWeaklyReachable(object) is somewhat equivalent to assert-dead(object) in GC Assertions: Using the Garbage Collector to Check Heap Properties. The LeakChaser paper argues that it can be inconvenient because:
a reachability-based assertion such as assertDead in prior work has to be placed in a location where the asserted object is about to become unreachable
The paper suggests implementing assert dies before instead, to assert that object A dies before object B immediately after they are created.
In the paper this is implemented in the GC, and we can't do that. However, we could implement this using weak refs, and essentially assert that B must become weakly reachable before A.
In practice, the method call takes A and B and adds two weak refs to the ref queue. Unlike the current approach where we expect "dead" objects to become weakly reachable soon, there's no clear expected timeline here, so we'd need a repeated scheduled check of the reference queue. Once we find A in the queue, we first finish emptying it, and if we can't find B then we can say that B is leaking because A has become weakly reachable and B has not. At this point we can count B as a retained object and trigger LeakCanary all the same.
ReachabilityWatcher is a functional (SAM) interface so we can't really add a method there, but anyway it's probably something like expectWeaklyReachableBefore(trigger: Any, watchedObject:Any, description: String)
This doesn't seem too hard to implement, however the hardest piece is: what could we use this for?
- expectWeaklyReachableBefore(activity, view) : this is a valid expectation, however we already check that early because all views reference an activity and we expect the activity to become unreachable in on destroy.
- expectWeaklyReachableBefore(parentView, childView) : for views that are detached before activity destroyed. Unfortunately that can't work because parent and child are always connected both ways, so they become weakly reachable at the same time.
- expectWeaklyReachableBefore(screen, screenRootView) : So first there's still a risk that views reference the screen they're associated with, making this pointless. Even if they don't, most screen / scoping framework have lifecycle callbacks that are betters signals for "this should be dead now".