kotlin关键字对象创建的单例类会被JVM回收吗?
object MySingleton
Run Code Online (Sandbox Code Playgroud)
如果MySingleton从未使用过,那么在应用程序运行时它是否会继续占用内存?
前言:这个答案只讨论针对 JVM 的 Kotlin。
\n如果MySingleton从未使用过,则永远不会加载它,因此永远不会占用任何内存。但是,如果您询问单例在一段时间不使用后(在至少使用过一次之后)是否会被垃圾收集,那么答案基本上是否定的。
假设您有:
\nobject MySingleton\nRun Code Online (Sandbox Code Playgroud)\n然后这就是object编译后的内容:
Classfile [omitted]\n Last modified Mar 9, 2023; size 631 bytes\n SHA-256 checksum ea5757d5e8673ae4a7153fba4699771529e5db7a28a72c92378c21f6dc7fbc29\n Compiled from "MySingleton.kt"\npublic final class MySingleton\n minor version: 0\n major version: 63\n flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER\n this_class: #2 // MySingleton\n super_class: #4 // java/lang/Object\n interfaces: 0, fields: 1, methods: 2, attributes: 2\nConstant pool:\n #1 = Utf8 MySingleton\n #2 = Class #1 // MySingleton\n #3 = Utf8 java/lang/Object\n #4 = Class #3 // java/lang/Object\n #5 = Utf8 <init>\n #6 = Utf8 ()V\n #7 = NameAndType #5:#6 // "<init>":()V\n #8 = Methodref #4.#7 // java/lang/Object."<init>":()V\n #9 = Utf8 this\n #10 = Utf8 LMySingleton;\n #11 = Utf8 <clinit>\n #12 = Methodref #2.#7 // MySingleton."<init>":()V\n #13 = Utf8 INSTANCE\n #14 = NameAndType #13:#10 // INSTANCE:LMySingleton;\n #15 = Fieldref #2.#14 // MySingleton.INSTANCE:LMySingleton;\n #16 = Utf8 Lorg/jetbrains/annotations/NotNull;\n #17 = Utf8 Lkotlin/Metadata;\n #18 = Utf8 mv\n #19 = Integer 1\n #20 = Integer 8\n #21 = Integer 0\n #22 = Utf8 k\n #23 = Utf8 xi\n #24 = Integer 48\n #25 = Utf8 d1\n #26 = Utf8 \\u0000\\f\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\b\xe2\x95\x9e\\u0002\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002\xc3\xb3\\u0006\\u0002\\u0010\\u0002\xc2\xbf\\u0006\\u0003\n #27 = Utf8 d2\n #28 = Utf8\n #29 = Utf8 [omitted]\n #30 = Utf8 MySingleton.kt\n #31 = Utf8 RuntimeInvisibleAnnotations\n #32 = Utf8 Code\n #33 = Utf8 LineNumberTable\n #34 = Utf8 LocalVariableTable\n #35 = Utf8 SourceFile\n #36 = Utf8 RuntimeVisibleAnnotations\n{\n public static final MySingleton INSTANCE;\n descriptor: LMySingleton;\n flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL\n RuntimeInvisibleAnnotations:\n 0: #16()\n org.jetbrains.annotations.NotNull\n\n private MySingleton();\n descriptor: ()V\n flags: (0x0002) ACC_PRIVATE\n Code:\n stack=1, locals=1, args_size=1\n 0: aload_0\n 1: invokespecial #8 // Method java/lang/Object."<init>":()V\n 4: return\n LineNumberTable:\n line 1: 0\n LocalVariableTable:\n Start Length Slot Name Signature\n 0 5 0 this LMySingleton;\n\n static {};\n descriptor: ()V\n flags: (0x0008) ACC_STATIC\n Code:\n stack=2, locals=0, args_size=0\n 0: new #2 // class MySingleton\n 3: dup\n 4: invokespecial #12 // Method "<init>":()V\n 7: putstatic #15 // Field INSTANCE:LMySingleton;\n 10: return\n}\nSourceFile: "MySingleton.kt"\nRuntimeVisibleAnnotations:\n 0: #17(#18=[I#19,I#20,I#21],#22=I#19,#23=I#24,#25=[s#26],#27=[s#10,s#28,s#6,s#29])\n kotlin.Metadata(\n mv=[1,8,0]\n k=1\n xi=48\n d1=["\\u0000\\f\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\b\xe2\x95\x9e\\u0002\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002\xc3\xb3\\u0006\\u0002\\u0010\\u0002\xc2\xbf\\u0006\\u0003"]\n d2=["LMySingleton;","","()V", "[omitted]"]\n )\n\nRun Code Online (Sandbox Code Playgroud)\n正如您所看到的,单例的实例保存在public, static,final字段中,并且该字段在类初始化时分配。因此,除非该对象被垃圾回收,否则单例的实例不会被Class垃圾回收。并且Class除非其关联对象被垃圾收集,否则该对象不会ClassLoader被垃圾收集。
ClassLoader但 a被垃圾收集的情况很少见。事实上,我非常有信心它永远不会发生在“标准”应用程序中。请记住,如果类加载器加载的任何类仍在“使用中”,则该类加载器被强引用,因此不符合垃圾回收的条件。
我怀疑只有在管理自己的类加载器的应用程序/框架/容器中,目的是让它们在某些情况下被垃圾收集,你才会看到被ClassLoader垃圾收集。尽管很难确保不存在对按需类加载器的强引用。即使这样,也假设垃圾收集器被允许和/或强制收集类和类加载器。请参阅霍尔格的评论。
因此,单例实例极有可能在应用程序的整个生命周期中保存在内存中。你应该认为这是确定的。
\n既然您问过它,这同样适用于顶级变量。如果名为 的源文件中有以下内容Foo.kt:
val bar = Any()\nRun Code Online (Sandbox Code Playgroud)\n然后这就是它的编译结果:
\nClassfile [omitted]\n Last modified Mar 10, 2023; size 629 bytes\n SHA-256 checksum 35d96f7a51c14816b96102d40d466d14904c19162b4797ef7cbc08333b70394c\n Compiled from "Foo.kt"\npublic final class FooKt\n minor version: 0\n major version: 63\n flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER\n this_class: #2 // FooKt\n super_class: #4 // java/lang/Object\n interfaces: 0, fields: 1, methods: 2, attributes: 2\nConstant pool:\n #1 = Utf8 FooKt\n #2 = Class #1 // FooKt\n #3 = Utf8 java/lang/Object\n #4 = Class #3 // java/lang/Object\n #5 = Utf8 getBar\n #6 = Utf8 ()Ljava/lang/Object;\n #7 = Utf8 Lorg/jetbrains/annotations/NotNull;\n #8 = Utf8 bar\n #9 = Utf8 Ljava/lang/Object;\n #10 = NameAndType #8:#9 // bar:Ljava/lang/Object;\n #11 = Fieldref #2.#10 // FooKt.bar:Ljava/lang/Object;\n #12 = Utf8 <clinit>\n #13 = Utf8 ()V\n #14 = Utf8 <init>\n #15 = NameAndType #14:#13 // "<init>":()V\n #16 = Methodref #4.#15 // java/lang/Object."<init>":()V\n #17 = Utf8 Lkotlin/Metadata;\n #18 = Utf8 mv\n #19 = Integer 1\n #20 = Integer 8\n #21 = Integer 0\n #22 = Utf8 k\n #23 = Integer 2\n #24 = Utf8 xi\n #25 = Integer 48\n #26 = Utf8 d1\n #27 = Utf8 \\u0000\\n\\n\\u0000\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0003\\"\\u0011\\u0010\\u0000\\u001a\\u00020\\u0001\xc3\xb3\\u0006\\b\\n\\u0000\\u001a\\u0004\\b\\u0002\\u0010\\u0003\xc2\xbf\\u0006\\u0004\n #28 = Utf8 d2\n #29 = Utf8\n #30 = Utf8 [omitted]\n #31 = Utf8 Foo.kt\n #32 = Utf8 RuntimeInvisibleAnnotations\n #33 = Utf8 Code\n #34 = Utf8 LineNumberTable\n #35 = Utf8 SourceFile\n #36 = Utf8 RuntimeVisibleAnnotations\n{\n private static final java.lang.Object bar;\n descriptor: Ljava/lang/Object;\n flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL\n RuntimeInvisibleAnnotations:\n 0: #7()\n org.jetbrains.annotations.NotNull\n\n public static final java.lang.Object getBar();\n descriptor: ()Ljava/lang/Object;\n flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL\n Code:\n stack=1, locals=0, args_size=0\n 0: getstatic #11 // Field bar:Ljava/lang/Object;\n 3: areturn\n LineNumberTable:\n line 1: 0\n RuntimeInvisibleAnnotations:\n 0: #7()\n org.jetbrains.annotations.NotNull\n\n static {};\n descriptor: ()V\n flags: (0x0008) ACC_STATIC\n Code:\n stack=2, locals=0, args_size=0\n 0: new #4 // class java/lang/Object\n 3: dup\n 4: invokespecial #16 // Method java/lang/Object."<init>":()V\n 7: putstatic #11 // Field bar:Ljava/lang/Object;\n 10: return\n LineNumberTable:\n line 1: 0\n}\nSourceFile: "Foo.kt"\nRuntimeVisibleAnnotations:\n 0: #17(#18=[I#19,I#20,I#21],#22=I#23,#24=I#25,#26=[s#27],#28=[s#8,s#29,s#5,s#6,s#30])\n kotlin.Metadata(\n mv=[1,8,0]\n k=2\n xi=48\n d1=["\\u0000\\n\\n\\u0000\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0003\\"\\u0011\\u0010\\u0000\\u001a\\u00020\\u0001\xc3\xb3\\u0006\\b\\n\\u0000\\u001a\\u0004\\b\\u0002\\u0010\\u0003\xc2\xbf\\u0006\\u0004"]\n d2=["bar","","getBar","()Ljava/lang/Object;","[omitted]"]\n )\n\nRun Code Online (Sandbox Code Playgroud)\n顶级变量被编译为名为 的类的private, static,字段,并且该字段在类初始化时被赋值。由于在本例中该字段是私有的,因此您会注意到有一个,finalFooKtpublicstatic getter 方法可以访问它。由于该字段是静态的,因此与上面关于何时以及是否进行垃圾收集的推理相同。
| 归档时间: |
|
| 查看次数: |
127 次 |
| 最近记录: |