Do companion objects remain in memory for app's lifecycle

instance of the class (MyClass) remains in memory for the entire lifecycle of the app?

Companion object in JVM

in kotlin

class MyClass {
   companion object {
        fun doSomething() {

        }
    }
}

MyClass(Kotlin) converted in JVM

public final class MyClass {
   public static final MyClass.Companion Companion = new MyClass.Companion(null);

   public static final class Companion {
      public final void doSomething() {
      }

      private Companion() {
      }

      public Companion() {
         this();
      }
   }
}

As above code, companion object is declared as Companion class in JVM, and it's created as static field inside MyClass class. Thus, isn't collected by gc. So, the memory of object(Companion) is remained during the ProcessLifecycle. static final object isn't released in normal case.

In conclusion, if referred to MyClass.Companion instance in application, that instance will not be garbage collected. (on general Classloaders).

*If not referred to MyClass.Companion instance in application, it may be removed by code shrinking feature.

Is there a way in Android Studio to check to see if this is the case?

You can see through android studio > profiler > Heap dump.

Reference

  • https://developer.android.com/topic/performance/memory-overview
  • https://developer.android.com/studio/build/shrink-code

As you seem to know and the above answer also makes clear that companion objects are translated to classes, and the class which declares them holds a static reference to the object of companion class, something as following:

public static final MyClass.Companion Companion = new MyClass.Companion(null);

Now the question

Do companion objects remain in memory for app's lifecycle

because the declaring class holds a static reference to companion class, the question reduces to the life time of static fields in jvm class and the answer lies in the JVM spec, but the spec is bit dry on the explanation so I am adding some snippets from the book Inside the Java Virtual Machine.

As in your example let say we have a class with nothing but single companion object.

First question is when an object of companion class will be created ? or when static fields are initialized ?

relevant text from the book. (for context the book is talking about class loading procedure)

Initialization

The final step required to ready a class or interface for its first active use is initialization, the process of setting class variables to their proper initial values. As used here, a "proper" initial value is the programmerís desired starting value for a class variable. A proper initial value contrasts with the default initial value given to class variables during preparation. As described above, the virtual machine assigns default values based only on each variableís type. Proper initial values, by contrast, are based on some master plan known only to the programmer. In Java code, a proper initial value is specified via a class variable initializer or static initializer.

So we know that once MyClass is loaded and initialized, the object of companion class will be created.

but what would cause JVM to load MyClass ?

The Java Virtual Machine specification gives implementations flexibility in the timing of class and interface loading and linking, but strictly defines the timing of initialization. All implementations must initialize each class and interface on its first active use. An active use of a class is:

  1. The invocation of a constructor on a new instance of the class

  2. The creation of an array that has the class as its an element type

  3. The invocation of a method declared by the class (not inherited from a superclass)

4 The use or assignment of a field declared by the class (not inherited from a superclass or super interface), except for fields that are both static and final, and are initialized by a compile-time constant expression

So as per 4th point when you do MyClass.foo() from kotlin or MyClass.Companion.foo() at this point MyClass will be loaded and ready. (Probably a lot early)

Please note that at this point no object of MyClass exist, that is we haven't used expression MyClass().

Does this mean static fields will remain in memory as long as the application is running ?

They can be garbage collected if the declaring type gets unloaded, in our case if JVM or ART (on android) unloads the MyClass then there is a possibility that companion object will be Garbage collected.

JVM Spec has following to say about class unloading

An implementation of the Java programming language may unload classes.

A class or interface may be unloaded if and only if its defining class loader may be reclaimed by the garbage collector as discussed in §12.6.

Classes and interfaces loaded by the bootstrap loader may not be unloaded.

In practicality class unloading almost(I said almost) never happens, so yes companion objects will remain in memory for app's life cycle.

Tags:

Android

Kotlin