Java类引用类的加载机制

标签 java class jvm lazy-loading classloader

当加载类A时,假设A的字节码引用了许多其他类。假设类 A 如下所示。

class A extends B implements C,D {

    static E e;

    F f;

    A() {

      G g = new G();

    }

    void print(H h) {

    }
}

JVM 是否加载 A 使用的所有类?或者在类初始化之前它不会打扰它们?

如果至少加载了其中一些,它们是否会在 A 完成后加载?或者 A 的加载将暂停,直到加载所需的类?

对于这个问题,假设尚未加载任何其他类。甚至父类(super class) B 和接口(interface) CD

最佳答案

要理解这一点,让我们了解一些基础知识。这将帮助任何新手理解 JAVA 中的延迟加载。

如果您熟悉 Netscape 的 Web 浏览器并且使用过 3.x 和 4.x 版本,那么您无疑会注意到 Java 运行时加载方式的差异。如果您查看 Netscape 3 启动时的初始屏幕,您会注意到它加载了各种资源,包括 Java。然而,当您启动 Netscape 4.x 时,它不会加载 Java 运行时 — 它会等到您访问包含该标记的网页为止。这两种方法说明了急切实例化(在需要时加载它)和惰性实例化(等到请求后再加载它,因为它可能永远不需要)的技术。

这两种方法都有缺点:一方面,如果在 session 期间不使用资源,则始终加载资源可能会浪费宝贵的内存;另一方面,如果尚未加载,则需要根据首次需要该资源时的加载时间来付出代价。

将延迟实例化视为一种资源节约策略

Java 中的延迟实例化分为两类:

  • 延迟类加载
  • 列出项目

延迟类加载

Java 运行时具有内置的类延迟实例化。类仅在首次引用时才加载到内存中。 (它们也可以首先通过 HTTP 从 Web 服务器加载。)

 MyUtils.classMethod();   //first call to a static class method
 Vector v = new Vector(); //first call to operator new

延迟类加载是Java运行时环境的一个重要特性,因为它可以在某些情况下减少内存使用。例如,如果程序的一部分在 session 期间从未执行,则仅在程序的该部分中引用的类永远不会被加载。

延迟对象创建

延迟对象创建与延迟类加载紧密耦合。第一次在以前未加载的类类型上使用 new 关键字时,Java 运行时将为您加载它。与延迟类加载相比,延迟对象创建可以更大程度地减少内存使用。

为了介绍延迟对象创建的概念,让我们看一个简单的代码示例,其中 Frame 使用 MessageBox 来显示错误消息:

   public class MyFrame extends Frame
  {
  private MessageBox mb_ = new MessageBox();
  //private helper used by this class
  private void showMessage(String message)
  {
    //set the message text
    mb_.setMessage( message );
    mb_.pack();
    mb_.show();
  }
}

在上面的例子中,当创建MyFrame的实例时,MessageBox实例mb_也被创建。相同的规则递归地应用。因此,在类 MessageBox 的构造函数中初始化或分配的任何实例变量也会从堆中分配,依此类推。如果 MyFrame 实例不用于在 session 中显示错误消息,我们就会不必要地浪费内存。

在这个相当简单的示例中,我们实际上不会获得太多 yield 。但是,如果您考虑一个更复杂的类,它使用许多其他类,而这些类又递归地使用和实例化更多对象,则潜在的内存使用量会更加明显。

  public final class MyFrame extends Frame
  {
  private MessageBox mb_ ; //null, implicit
  //private helper used by this class
  private void showMessage(String message)
  {
    if(mb_==null)//first call to this method
      mb_=new MessageBox();
    //set the message text
    mb_.setMessage( message );
    mb_.pack();
    mb_.show();
  }
}

如果仔细观察 showMessage(),您会发现我们首先确定实例变量 mb_ 是否等于 null。由于我们没有在声明时初始化 mb_,Java 运行时已经为我们处理了这个问题。因此,我们可以安全地继续创建 MessageBox 实例。以后所有对 showMessage() 的调用都会发现 mb_ 不等于 null,因此会跳过对象的创建并使用现有实例。

结论:一旦子实体初始化了所有依赖对象,它就会加载所有依赖对象。为了减少内存占用,我们应该在设计模式中仔细寻找这些类型,例如,虚拟代理,延迟初始化

关于Java类引用类的加载机制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55492005/

相关文章:

java - Wicket 替换 ListView 中的项目

java - 如何使用 Java ZoneId 的区域设置?

java - 将 .class 文件转换为 .jar 文件的 .java 文件

java - JVM 是否会在分布式/并行处理中产生大量开销?

android - 无法识别的 VM 选项 'MaxPermSize=512m' 错误 : Could not create the Java Virtual Machine. 错误 : A fatal exception has occurred. 程序将退出

java - Selenium WebDriver - 什么是 "Selenium Client & WebDriver Language Bindings"

java - 将 "any"类类型传递到 java 方法中

javascript - 如何在 Javascript 中制作自定义对象?

Java:非堆内存分析

java - 在java中安排批处理文件每2分钟运行一次