【问题标题】:Can we ever have two copies of an Static field in JAVA我们可以在 JAVA 中拥有两个静态字段副本吗
【发布时间】:2014-03-07 14:08:13
【问题描述】:

我明白了

  1. 静态字段属于/关联到 CLASS 类型
  2. 该类的所有对象都使用它
  3. 如果该类由同一个 JVM 中的两个不同的类加载器加载,那么我们可以有两个副本。

有什么方法/场景可以让我拥有两个具有不同值的静态副本?

【问题讨论】:

  • 是的,如果你有两个类加载器,根据第 3 点。
  • 此外,当多线程处理时,对变量的值更改可能不会立即对另一个线程可见。所以在某种程度上,静态字段可以同时有 2 个值(在不同的核心缓存中)
  • 真的不是两份。考虑两个类 B 和 D 的情况,它们都有一个静态字段 foo。我们会说我们有两个 foo 副本吗?当然不是,当我们有两个加载器加载“相同”的类时,情况也是如此:这两个类被视为与 B 和 D 不同。

标签: java oop static classloader permgen


【解决方案1】:

是的,您可以为static final 原语提供这种情况。

让我们考虑以下情况:

  1. A 类有public static final int SOME_CONSTANT = 1
  2. B 类参考A.SOME_CONSTANT
  3. A 类和 B 类已编译
  4. B 类更改为public static final int SOME_CONSTANT = 2
  5. B 类被重新编译而不重新编译 A 类

如果你现在要启动一个 JVM(在类路径上有 A 和 B),那么类 A 中的静态值将为 1,而类 B 的值为 2。这是因为 javac 编译了一个将值复制到每个 .class 文件中。 B 类有它自己的静态副本,并且不引用 A 类。要解决此问题,您必须在每次 B 类中的常量更改时重新编译 A 类(即进行干净构建而不是增量构建)。请注意,此问题仅适用于 static final 原语(int、long 等)。

【讨论】:

  • 嗯,非常感谢您偶然发现这个问题的洞察力。但是这种情况是否会发生,即一个 A 类已经加载,而 Blass B 卸载并重新加载并以相同的方式进行通信?它也很像再次加载一个类,类似于使用 2 类加载器加载 2 类。所以我的理解是,在正常情况下,两个线程不能拥有同一个 STATIC 字段的两个不同值。
  • 请注意,这不是运行时问题。这是一个编译时问题。它与类加载器无关,是由编译后的.class文件包含不同的值引起的。
【解决方案2】:

如果您的 C 类由两个不同的类加载器 CL1 和 CL2 加载,
那么java.lang.Class 的两个实例 c1、c2 描述了您的
C类。然后在每个(c1,c2)中,您可以有不同的值
在您的静态字段 f 中(f 在 C 中定义)。

有些人甚至认为从技术上讲这两个 c1、c2
就 JVM 而言,它们是不同的类。

【讨论】:

  • 确实如此,这两个 C 类被 JVM 视为两个完全不相关的类。出于这个原因,情况与两个类相同,例如 B 和 D,它们恰好有一个静态字段 foo
  • 是的,我知道。实际上 c1 和 c2 在不同的时间可能是 C 类(我的意思是源文件的编译版本)。所以这两个类不同是正常的,因为字节码可能完全不同。
  • 非常感谢 peter.petrov,Ingo,Richard Tingle。
  • 但是如果我只有一个类加载器,那么我可以通过任何方式获得我的静态字段的两个副本吗?
  • 并非如此,只是在 Richard Tingle 所指的意义上。但这些实际上是同一个静态变量的两个线程缓存副本。共享静态字段中的值只有一个(我这里说的是引用类型静态字段)。
猜你喜欢
  • 2021-01-11
  • 2021-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-30
  • 2019-12-02
  • 1970-01-01
  • 2012-01-14
相关资源
最近更新 更多