伴生对象是否在应用程序的生命周期中保留在内存中

And*_*Dev 11 android kotlin

在 Kotlin 中,您可以使用伴随对象创建单例:

class MyClass {
   companion object {
        fun doSomething() {

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

根据 Kotlin 文档,它指出:

请注意,即使伴随对象的成员在其他语言中看起来像静态成员,但在运行时它们仍然是真实对象的实例成员......

https://kotlinlang.org/docs/reference/object-declarations.html

这是否意味着在伴随对象中使用函数后,类(MyClass)的实例在应用程序的整个生命周期中都保留在内存中?Android Studio 中有没有办法检查是否是这种情况?

Eth*_*hoi 11

类 (MyClass) 的实例在应用程序的整个生命周期中都保留在内存中?

JVM 中的伴生对象

在科特林

class MyClass {
   companion object {
        fun doSomething() {

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MyClass(Kotlin) 在 JVM 中转换

class MyClass {
   companion object {
        fun doSomething() {

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如上代码,在 JVM 中companion object声明为Companion类,并static在MyClass类中创建为字段。因此,不是由 gc 收集的。因此,对象(同伴)的内存在ProcessLifecycle. static final object正常情况下不释放。

总之,如果MyClass.Companion在应用程序中引用实例,该实例将不会被垃圾收集。(在一般类加载器上)。

*如果MyClass.Companion在应用程序中没有引用实例,它可能会被代码收缩功能删除。

Android Studio 中有没有办法检查是否是这种情况?

可以通过android studio > profiler > Heap dump查看。

参考

  • 您可以使用android studio将kotlin转换为字节码并反编译它。 (2认同)

mig*_*WOZ 10

正如您似乎知道的那样,上面的答案也清楚地表明,伴生对象被转换为类,并且声明它们的类持有对伴生类对象的静态引用,如下所示:

\n\n
public static final MyClass.Companion Companion = new MyClass.Companion(null);\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在问题

\n\n
\n

在应用程序的生命周期中,伴随对象是否保留在内存中

\n
\n\n

因为声明类持有static对伴生类的引用,所以问题减少到了staticjvm 中字段的生命周期class,答案就在JVM 规范中,但规范的解释有点干巴巴的,所以我添加了《Inside》一书中的一些片段Java 虚拟机。

\n\n

就像在你的例子中一样,假设我们有一个class只有一个伴生对象的东西。

\n\n

第一个问题是何时创建伴生类的对象? 或\n 当static字段初始化时?

\n\n

书中相关文字。(对于上下文,本书正在讨论类加载过程)

\n\n
\n

初始化

\n\n

准备好类或接口以供首次使用所需的最后一步是初始化,即将类变量设置为其正确初始值的过程。如此处所用,“正确”初始值是程序员\n xc3\xads 类变量所需的\n 起始值。正确的初始值与准备期间为类变量指定的默认初始值形成对比。如上所述,虚拟机仅根据每个变量xc3xads 类型分配默认值。相比之下,正确的初始值基于一些只有程序员知道的总体规划。在 Java 代码中,正确的初始值是通过类变量初始化器或静态初始化器指定的。

\n
\n\n

所以我们知道,一旦MyClass加载并初始化,伴生类的对象就会被创建。

\n\n

但是什么会导致 JVM 加载呢MyClass?

\n\n
\n

Java 虚拟机规范在类和接口加载和链接的计时方面为实现提供了灵活性,但严格定义了初始化的计时。所有实现都必须在首次使用时初始化每个类和接口。类的主动使用是:

\n\n
    \n
  1. 在类的新实例上调用构造函数

  2. \n
  3. 创建一个数组,该数组的元素类型为\n

  4. \n
  5. 调用由类声明的方法\n(不是从超类继承的)

  6. \n
\n\n

4 使用或分配由类声明的字段(不是从超类或超接口继承的),静态和最终字段除外,并且由编译时常量表达式初始化

\n
\n\n

因此,根据第四点,当您MyClass.foo()从 kotlin 执行此操作时,或者MyClass.Companion.foo()此时MyClass将被加载并准备就绪。(可能还早很多)

\n\n

请注意,此时不MyClass存在任何对象,即我们还没有使用过表达式MyClass()。

\n\n

这是否意味着static只要应用程序正在运行,字段就会保留在内存中?

\n\n

如果声明类型被卸载,它们可能会被垃圾收集,在我们的例子中,如果 JVM 或 ART(在 Android 上)卸载,那么MyClass伴生对象就有可能被垃圾收集。

\n\n

JVM 规范对类卸载有以下规定

\n\n
\n

Java 编程语言的实现可以卸载类。

\n\n

当且仅当其定义类加载器可以被垃圾收集器回收时,类或接口才可以被卸载,如\n\xc2\xa712.6 中所述。

\n\n

引导加载程序加载的类和接口可能无法卸载。

\n
\n\n

实际上,类卸载几乎(我说几乎)永远不会发生,所以是的,伴生对象将在应用程序的生命周期中保留在内存中。

\n