由于 class 卸载,静态变量可以多次初始化吗?

Can static variable be initialized many times, because of class unloading?

大家好,当我看到关于 G1GC 的介绍时,我了解到,这个 GC 可以在收集垃圾期间卸载 class。让我们想象一下,我们有这样一个 class.

public class Foo {
   public static int a = 5;

}

并假设我们在代码中没有对这个 class 的任何引用,以便让 GC 清楚地知道这个 class 不会再被使用。为了使 class 加载和初始化发生,我们定期访问这个 int a 变量。为了在代码中没有任何引用,我们通过反射在 http 请求调用的方法中使用 class 名称作为参数来添加此变量。

在这种情况下或任何类似情况下,class 是否可能被初始化多次,这也意味着静态字段将被初始化多次?

静态字段可以初始化几次吗?

您是否尝试过使用 单例 设计模式创建持久对象?

单例是class对象,在整个应用程序中是唯一的,它是在第一次实例化时创建的[=26] =].

只要应用程序 运行ning 和任何实例化 returns 当前实例或新实例(如果之前未实例化),就会存在单例。 如果你想要一个新实例,你可以在单例 class 上实现一个方法来销毁当前实例并 returns 一个新实例。 我使用单例来存储设置以及访问和凭据管理。

这是关于单例的 link 到 post: Singleton Design Pattern

如果您没有 运行 多线程,您可以使用 post 中描述的 方法 1,这是我经常做的。 但是,如果您 运行 多个线程,我建议使用 post 中提到的最后一种方法,因为它声称是最好的,尽管我还没有测试它。

§12.7. Unloading of Classes and Interfaces 的规范中解决了这个问题:

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.

规则的动机与您的问题直接相关:

Class unloading is an optimization that helps reduce memory use. Obviously, the semantics of a program should not depend on whether and how a system chooses to implement an optimization such as class unloading. To do otherwise would compromise the portability of programs. Consequently, whether a class or interface has been unloaded or not should be transparent to a program.

Reloading may not be transparent if, for example, the class has static variables (whose state would be lost), static initializers (which may have side effects), or native methods (which may retain static state). Furthermore, the hash value of the Class object is dependent on its identity. Therefore it is, in general, impossible to reload a class or interface in a completely transparent manner.

Since we can never guarantee that unloading a class or interface whose loader is potentially reachable will not cause reloading, and reloading is never transparent, but unloading must be transparent, it follows that one must not unload a class or interface while its loader is potentially reachable. A similar line of reasoning can be used to deduce that classes and interfaces loaded by the bootstrap loader can never be unloaded.

除了明确提到的 bootstrap 加载器(它需要一个显式声明,因为它由 null 表示,所以可达性是没有意义的),应用程序 class 加载器,也被称为system class loader is always reachable. The same applies to its parent, the extension class loader prior to JDK 9 and the platform class loader从那时起。

这意味着对于应用程序加载器加载的普通应用程序及其所有直接依赖项,class 卸载是不可能的。只有应用程序或框架创建的其他 class 个加载程序可能无法访问。

上面引用的规范还说:

Class unloading is an optimization that is only significant for applications that load large numbers of classes and that stop using most of those classes after some time. A prime example of such an application is a web browser, but there are others. A characteristic of such applications is that they manage classes through explicit use of class loaders. As a result, the policy outlined above works well for them.

这个例子反映了 Java 的历史,但今天,最好的例子是 application server。当重新部署新版本时,甚至当使用新的 class 加载程序再次加载相同的 class 文件时,它们在技术上是不同的 classes 并且卸载的透明度无关紧要。

这包含在虚拟机规范中,§5.3. Creation and Loading 声明:

At run time, a class or interface is determined not by its name alone, but by a pair: its binary name (§4.2.1) and its defining class loader.


一个特例是通过新 defineHiddenClass method 创建的 classes。由于那些 classes 无法通过其定义的加载程序按名称解析,因此可以在不存在对它的引用时立即卸载隐藏的 class。它无法再次重新加载。但是您可以使用相同的定义来创建任意数量的相同隐藏 classes,因为它们不会干扰。