java - 加载、链接和初始化——什么时候加载一个类?

标签 java jvm classloader bytecode static-initializer

我对类加载的理解是,类在第一次需要时加载(用一种非常简单的方式来说)。使用 -verbose:class 和 Iterators 类的修改版本运行以下示例,该版本在调用其 clinit 时打印一条消息我观察到一些我无法真正解释的东西:

public class IteratorsTest
{
    public static void main(String[] args)
    {
        com.google.common.collect.Iterators.forArray(1, 2, 3);
    }
}

(清理后的)输出如下:

[Loaded com.google.common.collect.Iterators from file:...]
[Loaded com.google.common.collect.Iterators$1 from file:...]
---------> Iterators <clinit>

为什么在调用 clinit 之前加载 Iterators$1?它只在 clinit 中定义,不是吗?

  static final UnmodifiableListIterator<Object> EMPTY_LIST_ITERATOR =
      new UnmodifiableListIterator<Object>() {
  ...
  }

这导致以下字节码:

static <clinit>()V
   L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "---------> Iterators clinit --------------"**
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    NEW com/google/common/collect/Iterators$1
    DUP
    INVOKESPECIAL com/google/common/collect/Iterators$1.<init> ()V
   L2
    PUTSTATIC com/google/common/collect/Iterators.EMPTY_LIST_ITERATOR : Lcom/google/common/collect/UnmodifiableListIterator;

更让我感到困惑的是,我还有一个示例(太复杂了,无法在此处发布),其中与上面主要部分相同的代码行导致以下输出:

[Loaded com.google.common.collect.Iterators from file:...]
---------> Iterators <clinit>
[Loaded com.google.common.collect.Iterators$1 from file:...]

这实际上也是我对简单测试程序的期望。

我试图在这里找到答案 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html ,但这并没有真正帮助。

  • 有时先执行 clinit 而有时先加载匿名类的原因可能是什么?
  • 有没有办法跟踪 JVM 何时调用类的 clinit?类似于 -verbose:class 或 -XX:+TraceClassLoading 等的东西?

最佳答案

What could be the reason for sometimes the clinit being executed first and sometimes the anonymous class being loaded first?

类加载过程包含以下过程。

  • 加载中
  • 链接
    • 验证
    • 准备
    • 决议
  • 初始化
  • 使用
  • 卸载

现在我们关注的是解析初始化阶段 引用类加载发生在解析阶段发生在初始化阶段。 加载验证准备初始化卸载的顺序是固定的,但是调用解析阶段的时间不固定,可能发生在初始化之前(对应你的前一种情况)阶段,它也可能在某些情况下发生在初始化之后(对应你的后一种情况)。

为了性能,HotSpot VM 通常会等到类初始化时才加载和链接类。所以如果A类引用了B类,加载A类不一定会导致加载B类(除非需要验证)。执行引用 B 的第一条指令将导致 B 的初始化,这需要加载和链接类 B。

Is there a way to trace when the JVM invokes the clinit of the classes? something similar to -verbose:class or -XX:+TraceClassLoading, etc?

我不知道是否存在一些jvm 参数 可以获取jvm 直接调用 方法的时间,但还有另一种方法可以实现这一点,使用jvm_ti 。您可以监听一些事件,例如 methodEntry,然后获取调用 方法的时间。如需更多信息,请谷歌 jvm_ti

引用:

关于java - 加载、链接和初始化——什么时候加载一个类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41656506/

相关文章:

java - 使用Spring将文件保存到资源目录

java - 基于DOS的JVM可用

java - 为什么 final 是 Java 中局部变量的唯一修饰符?

osgi - 为什么导入的类优先于捆绑类?

java - 如何防止使用任意方法,例如 String.lastIndexOf()

java - 从 jar 中访问资源

java - RecyclerView 在运行应用程序时未显示

修改测试代码的Java工具

java - HashMap 。使用自定义类型作为值

java - SpringBoot sun.security.ssl.SSLSessionContextImpl 内存泄漏