Heap dump of the JVM

I do not observe anything of this screen directly. I do have global observers (keyboard shown/hidden, text field/view begin/end editing) that I register in applicationDidBecomeActive(...) and unregister in applicationWillResignActive(...) of my application delegate. The first screen does not have text fields or text views.

When I disable all global observers, the backtrace to the notification center is gone, but the iOS object for the screen still exists.

Note that at this point I have disabled almost all UI elements of the screens that I test with, but I still see that iOS and JVM objects are not being freed. Even though the JVM heap dump shows that there are no more incoming references from my own objects. I would expect that JVM peers would get garbage collected, and that that will decrease the reference count. If the iOS object is not disposed of at this point, and rertrieved again by Java code later, I expect a new JVM peer to be created because the previous one was garbage collected.

Considering you use notification server for your application delegate, the node to which the notification points to might be your application delegate which in turn transitively holds a reference to the stuck object.

Are you sure, your application delegate does not have any field that may capture (directly or indirectly) the view?

Definitely. I may have a variable somewhere that may hold on to a single screen/view, but I definitely do not have collections of screens/views in my own objects. See the instance of SplashScreen in my previous screenshot. It does not have incoming references from any of my classes.

If you want, I can send you a JVM heap dimp so you can inspect it yourself.

Okay, please send it over, then.

I am still looking into the hprof file you have sent over.

Could you please set an exceptional breakpoint to dalvik.system.CloseGuard and tell me at what positions it is getting thrown?

I set a breakpoint on the following line of warnIfOpen(), but it is never reached:

String message =
        ("A resource was acquired at attached stack trace but never released. "
         + "See java.io.Closeable for information on avoiding resource leaks.");

If I set a breakpoint at the start if warnIfOpen(), the debugger halts at this line. I can see that ENABLED is true, so I correctly enabled CloseGuard.

I also enabled all (Java) exception breakpoints. These get triggered a lot during start-up. If I disable them until after I visited each screen at least once, they are nog longer triggered. Not even when the application crashes eventually.

Are those Views implemented entirely in Java or mixed with ObjC?

There is only Kotlin code for those views. Here is an example:

class CommandBar private constructor(peer: Pointer) : UIStackView(peer) {
    @Selector("init")
    external override fun init(): CommandBar

    fun initialize(commandWidgetFactory: IosCommandWidgetFactory, vararg commands: Command) {
        setTranslatesAutoresizingMaskIntoConstraints(false)
        setAxis(UILayoutConstraintAxis.Horizontal)
        setDistribution(UIStackViewDistribution.FillEqually)
        setAlignment(UIStackViewAlignment.Fill)

        commands.forEach { command ->
            addArrangedSubview(commandWidgetFactory.create(command))
        }
    }

    companion object {
        @Selector("alloc")
        @JvmStatic external fun alloc(): CommandBar
    }
}

Could you please add a method breakpoint for

org.moe.natj.general.NatJ.removeStrongReference

right before the view should be released and then check if it gets invoked or not? If not, then that will probably mean that there is a reference somewhere from the native side.

We have created a reproduction project. It is available here: https://github.com/squins/moe-non-freed-objects-issue

Details are in README.md on github.

Can you have a look at it? Thanks in advance!

Hi Kees,

thanks for the test project, we will take a look on Monday.

Best Regards,
Gergely

1 Like

Hi Gergely,

Have had any chance to look at our sample project?

Thanks!

Best Regards,

Kees

Hello

I am currently looking into it.

Daniel

There were some issues in your code.

  • Because alloc methods return with strong ownership, bindings must be marked with @Owned annotations.
  • System.gc() in itself is too lazy, use Runtime.getRuntime().gc() instead.

I have created a pull request containing these modifications.

Hope this helps.

1 Like

Thanks a lot for your advice. The combination of adding the @Owned annotations, asking the runtime to collect garbage when switching screens, and the dismantling of the view hierarchy, have resolved a lot of the retained memory. I still see some memory being retained when I use specific screens, but I will look into that.