【问题标题】:Java Class loading mechanism for referenced Classes引用类的 Java 类加载机制
【发布时间】:2019-08-24 19:07:13
【问题描述】:

当类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's 加载将暂停,直到所需的类加载?

对于这个问题,假设尚未加载任何其他类。甚至超类B 和接口C & D

【问题讨论】:

  • 或者 A 的加载将暂停,直到加载所需的类? - 当然,首先需要加载所有依赖项,然后是需要它们的类。似乎你问类是否是延迟加载的,那么答案是肯定的
  • @Thilo 所以当A 被加载时,BCDEFH 被加载。 G 是在实例化A 的实例时加载的?基本上这意味着:在加载包含类时,加载任何成员的类型/方法签名中的超类、接口和任何类。但是执行代码中存在的那些(尚未加载的类)是在第一次面对时加载的?
  • @ArunRajagopal 是的。基本上要加载一个类,您需要能够绑定它的方法。
  • 我建议阅读this answerthis one。如果您还有更多的时间和耐心,这里的another one 希望对您有所帮助。
  • 不。我们只需要知道它被称为 H。无论它是由什么组成的都无关紧要。

标签: java class jvm lazy-loading classloader


【解决方案1】:

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

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

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

将惰性实例化视为一种资源节约策略

Java 中的惰性实例化分为两类:

  • 延迟类加载
  • 列表项

延迟类加载

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

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

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

延迟对象创建

延迟对象创建与延迟类加载紧密耦合。当您第一次对以前未加载的类类型使用 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 的实例不用于在会话中显示错误消息,我们就会不必要地浪费内存。

在这个相当简单的例子中,我们并不会真正获得太多。但是,如果您考虑一个更复杂的类,它使用许多其他类,而这些类又递归地使用和实例化更多对象,那么潜在的内存使用情况就更加明显了。

  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,因此会跳过对象的创建并使用现有的实例。

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

【讨论】:

  • 你是说不仅当MyFrame被加载或初始化时,MessageBox类直到jvm执行mb_ = new MessageBox();行才被加载?还有什么时候会加载Frame
  • 我的意思是,即使你初始化了 MyFrame 类,MessageBox 也不会被 JVM 初始化或加载到堆中。我们已经在代码中处理了这一点,JVM 也处理了这一点。当你初始化 MyFrame 时,JVM 将首先加载它的依赖类(引用)
  • 我的问题更多的是,当MyFrame类被加载到JVM中时,加载机制是否会暂停并首先将MessageBox加载到JVM中?我可能误会了。初始化和加载是否同时进行?
  • 是的,除非你在上面的例子中调用了那个方法。
猜你喜欢
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 2020-10-11
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
相关资源
最近更新 更多