Java类装入器是否保证不装入未使用的类?

时间:2022-03-31 19:37:26

Is there a guarantee that (the default, system) Java class loader doesn't attempt to load classes that aren't referred to in the code being run? A couple of examples of what I mean:

是否有保证(默认的、系统的)Java类加载器不尝试加载正在运行的代码中没有提到的类?我的意思有几个例子:

  • I'm using a framework.jar which I know to contain references to another library.jar's classes in it, but I'm using only such part of the framework that doesn't contain those references. Is it safe to leave library.jar out?
  • 我使用一个框架。jar,我知道它包含对另一个库的引用。jar的类在其中,但是我只使用不包含那些引用的框架的这一部分。离开图书馆安全吗?罐子?
  • Static blocks are run when a class is first loaded. If no running code contains references to a specific class, is it sure that it's static block is not run?
  • 静态块在第一次加载类时运行。如果没有正在运行的代码包含对特定类的引用,那么是否确定它的静态块没有运行?

Quickly testing it seems to work as assumed above, and it wouldn't make much sense to load unused classes anyway, but is there any guarantee on this?

快速地测试它就像上面假设的那样工作,不管怎样,加载未使用的类并没有多大意义,但是有什么保证吗?

Addition: It seems that my "static blocks are run when a class is first loaded" statement above is somewhat incorrect. It's definitely possible to load classes (one thing) without running them (another thing). So I'm interested in both cases; guarantees about classes not getting loaded, and not getting run.

此外,我的“静态块是在类首次加载时运行的”,上面的语句有些不正确。加载类(一件事)而不运行它们(另一件事)是完全可能的。我对这两种情况都感兴趣;保证类不会被加载,也不会被运行。

7 个解决方案

#1


7  

There is no such guarantee1 wrt the loading of the classes.

没有这样的保证1用于装载类。

However, you are guaranteed that static blocks won't be run prematurely. The events that trigger class initialization are specified in JLS 12.4.1.

但是,您可以保证静态块不会提前运行。在JLS 12.4.1中指定了触发类初始化的事件。

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

类或接口类型T将在第一次出现下列任何一种之前立即初始化:

  • T is a class and an instance of T is created.
  • T是一个类,并创建了一个T实例。
  • T is a class and a static method declared by T is invoked.
  • T是一个类,调用T声明的静态方法。
  • A static field declared by T is assigned.
  • 指定由T声明的静态字段。
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • 使用静态字段声明T领域并不是一个常数变量(§4.12.4)。
  • T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.
  • T是一个*类和一个断言语句(§14.10)词法嵌套在T是执行。

1 - It is observed that current generation Java implementations do not load classes unnecessarily, but that is not a guarantee. The only guarantees are what it written in the official specifications.

1 -可以看到,当前的Java实现没有不必要地装入类,但这并不是一个保证。唯一的保证就是它所写的官方规范。

#2


4  

The java specification states

java规范规定

The loading process is implemented by the class ClassLoader and its subclasses. Different subclasses of ClassLoader may implement different loading policies. In particular, a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together.

加载过程由类ClassLoader及其子类实现。类加载器的不同子类可以实现不同的加载策略。特别是,类装入器可以缓存类和接口的二进制表示,根据预期的用法预取它们,或者将一组相关类加载到一起。

So the classloader is free to prefetch classfiles.

所以类加载器可以*地预取类文件。

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

类或接口类型T将在第一次出现下列任何一种之前立即初始化:

  • T is a class and an instance of T is created.
  • T是一个类,并创建了一个T实例。
  • T is a class and a static method declared by T is invoked.
  • T是一个类,调用T声明的静态方法。
  • A static field declared by T is assigned.
  • 指定由T声明的静态字段。
  • A static field declared by T is used and the reference to the field is not a compile-time constant (§15.28). References to compile-time constants must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization.
  • 使用静态字段声明T对字段的引用并不是编译时常量(§15.28)。编译时常量的引用必须在编译时解析为编译时常量值的副本,因此使用此类字段不会导致初始化。

The static blocks will be executed only when the class is first used.

静态块将只在第一次使用类时执行。

#3


1  

I don't think there's any such guarantee. For one thing, I've seen code scanners which do things like processing annotations from whole package hierarchies/JARs during application startup; they'd violate that assumption right away.

我认为没有这样的保证。首先,我见过代码扫描器,它们在应用程序启动时处理来自整个包层次结构/ jar的注释;他们会立刻违反这个假设。

Why does this matter? You're usually after highly controllable system loading so anything where would matter would be somewhere where you'd want to force it anyway…

为什么这很重要?你通常是在高度可控的系统加载之后,所以任何重要的东西都会在你想要强迫它的地方……

#4


1  

The thing that pulls in classes is the references to them from Java byte code (which again may pull in other classes). If the classes you run, do not have any reference to class X, it will not be loaded.

引入类的东西是来自Java字节代码的对类的引用(这也可能会引入其他类)。如果您运行的类没有任何对类X的引用,那么它将不会被加载。

Note however that there is newer ways to register e.g. services through META-INF. Those classes need to be loaded too.

但是请注意,还有一些更新的方法可以通过META-INF注册服务。这些类也需要加载。

You can always run with "-verbose" to see the classes as they load - the order clearly shows that they are loaded when needed.

您总是可以使用“-verbose”运行以查看类在加载时的情况——订单清楚地显示它们是在需要时加载的。

#5


1  

If you are not using reflection, then you can statically check which classes are used using a dead-code removal tool, such as ProGuard. It will analyse your code and determine all the classes used. On the basis of that, it removes unused code, including unused code in libraries.

如果不使用反射,那么可以静态地检查使用死代码删除工具(如ProGuard)使用的类。它将分析您的代码并确定所使用的所有类。在此基础上,它将删除未使用的代码,包括库中未使用的代码。

If your code or libraries use reflection to load classes, then you will need to run a full coverage test of your application and log all the classes loaded, which you instruct ProGuard to keep.

如果您的代码或库使用反射来加载类,那么您将需要对应用程序运行完整的覆盖率测试,并记录所有已加载的类,您将指示ProGuard保持这些类。

#6


0  

There are no such guarantees as other posters have mentioned. But your question and your concern don't follow from each other. For you to leave library.jar out, you don't need such guarantees.

没有其他海报提到的那样的保证。但是你的问题和你的关心并不是相互联系的。让你离开图书馆。你不需要这样的保证。

There are a number of frameworks that discover the presence or absence of other frameworks during runtime. Ex: Commons-logging discovers a bunch of other frameworks. Spring web flow discovers what the scripting framework is (is it OGNL for instance) during runtime. These frameworks are obviously compiled using all dependent frameworks but they dont have to exist during runtime.

有许多框架可以在运行时发现其他框架的存在或缺失。例:common -logging发现了很多其他框架。Spring web flow在运行时发现了脚本框架(例如OGNL)。这些框架显然是使用所有依赖框架编译的,但它们在运行时不需要存在。

Hence it is perfectly acceptable to leave library.jar out during runtime.

因此,离开图书馆是完全可以接受的。在运行时jar。

#7


0  

Yes.

是的。

Think about the following. If you add the following code to that library.jar class:

考虑以下。如果您向该库添加以下代码。罐类:

 public ShutDown {static { System.exit(-1); }}

That code won't be loaded automatically by the default system, because none of the existing code has a reference or knows about the ShutDown class, nor had a way to load it, and the Java class loader don't just go around trying to load random classes from the jar.

默认系统不会自动加载这些代码,因为所有现有代码都没有引用或不知道ShutDown类,也没有办法加载它,而且Java类装入器也不会到处尝试从jar加载随机类。

The way classes are loaded are described in the previous answers, if you review them carefully none of them include a "If there is a class in the jar it would be loaded" of some sort.

在前面的答案中描述了类的加载方式,如果您仔细检查它们,它们中没有一个包含“如果jar中有一个类会被加载”之类的。

#1


7  

There is no such guarantee1 wrt the loading of the classes.

没有这样的保证1用于装载类。

However, you are guaranteed that static blocks won't be run prematurely. The events that trigger class initialization are specified in JLS 12.4.1.

但是,您可以保证静态块不会提前运行。在JLS 12.4.1中指定了触发类初始化的事件。

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

类或接口类型T将在第一次出现下列任何一种之前立即初始化:

  • T is a class and an instance of T is created.
  • T是一个类,并创建了一个T实例。
  • T is a class and a static method declared by T is invoked.
  • T是一个类,调用T声明的静态方法。
  • A static field declared by T is assigned.
  • 指定由T声明的静态字段。
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • 使用静态字段声明T领域并不是一个常数变量(§4.12.4)。
  • T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.
  • T是一个*类和一个断言语句(§14.10)词法嵌套在T是执行。

1 - It is observed that current generation Java implementations do not load classes unnecessarily, but that is not a guarantee. The only guarantees are what it written in the official specifications.

1 -可以看到,当前的Java实现没有不必要地装入类,但这并不是一个保证。唯一的保证就是它所写的官方规范。

#2


4  

The java specification states

java规范规定

The loading process is implemented by the class ClassLoader and its subclasses. Different subclasses of ClassLoader may implement different loading policies. In particular, a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together.

加载过程由类ClassLoader及其子类实现。类加载器的不同子类可以实现不同的加载策略。特别是,类装入器可以缓存类和接口的二进制表示,根据预期的用法预取它们,或者将一组相关类加载到一起。

So the classloader is free to prefetch classfiles.

所以类加载器可以*地预取类文件。

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

类或接口类型T将在第一次出现下列任何一种之前立即初始化:

  • T is a class and an instance of T is created.
  • T是一个类,并创建了一个T实例。
  • T is a class and a static method declared by T is invoked.
  • T是一个类,调用T声明的静态方法。
  • A static field declared by T is assigned.
  • 指定由T声明的静态字段。
  • A static field declared by T is used and the reference to the field is not a compile-time constant (§15.28). References to compile-time constants must be resolved at compile time to a copy of the compile-time constant value, so uses of such a field never cause initialization.
  • 使用静态字段声明T对字段的引用并不是编译时常量(§15.28)。编译时常量的引用必须在编译时解析为编译时常量值的副本,因此使用此类字段不会导致初始化。

The static blocks will be executed only when the class is first used.

静态块将只在第一次使用类时执行。

#3


1  

I don't think there's any such guarantee. For one thing, I've seen code scanners which do things like processing annotations from whole package hierarchies/JARs during application startup; they'd violate that assumption right away.

我认为没有这样的保证。首先,我见过代码扫描器,它们在应用程序启动时处理来自整个包层次结构/ jar的注释;他们会立刻违反这个假设。

Why does this matter? You're usually after highly controllable system loading so anything where would matter would be somewhere where you'd want to force it anyway…

为什么这很重要?你通常是在高度可控的系统加载之后,所以任何重要的东西都会在你想要强迫它的地方……

#4


1  

The thing that pulls in classes is the references to them from Java byte code (which again may pull in other classes). If the classes you run, do not have any reference to class X, it will not be loaded.

引入类的东西是来自Java字节代码的对类的引用(这也可能会引入其他类)。如果您运行的类没有任何对类X的引用,那么它将不会被加载。

Note however that there is newer ways to register e.g. services through META-INF. Those classes need to be loaded too.

但是请注意,还有一些更新的方法可以通过META-INF注册服务。这些类也需要加载。

You can always run with "-verbose" to see the classes as they load - the order clearly shows that they are loaded when needed.

您总是可以使用“-verbose”运行以查看类在加载时的情况——订单清楚地显示它们是在需要时加载的。

#5


1  

If you are not using reflection, then you can statically check which classes are used using a dead-code removal tool, such as ProGuard. It will analyse your code and determine all the classes used. On the basis of that, it removes unused code, including unused code in libraries.

如果不使用反射,那么可以静态地检查使用死代码删除工具(如ProGuard)使用的类。它将分析您的代码并确定所使用的所有类。在此基础上,它将删除未使用的代码,包括库中未使用的代码。

If your code or libraries use reflection to load classes, then you will need to run a full coverage test of your application and log all the classes loaded, which you instruct ProGuard to keep.

如果您的代码或库使用反射来加载类,那么您将需要对应用程序运行完整的覆盖率测试,并记录所有已加载的类,您将指示ProGuard保持这些类。

#6


0  

There are no such guarantees as other posters have mentioned. But your question and your concern don't follow from each other. For you to leave library.jar out, you don't need such guarantees.

没有其他海报提到的那样的保证。但是你的问题和你的关心并不是相互联系的。让你离开图书馆。你不需要这样的保证。

There are a number of frameworks that discover the presence or absence of other frameworks during runtime. Ex: Commons-logging discovers a bunch of other frameworks. Spring web flow discovers what the scripting framework is (is it OGNL for instance) during runtime. These frameworks are obviously compiled using all dependent frameworks but they dont have to exist during runtime.

有许多框架可以在运行时发现其他框架的存在或缺失。例:common -logging发现了很多其他框架。Spring web flow在运行时发现了脚本框架(例如OGNL)。这些框架显然是使用所有依赖框架编译的,但它们在运行时不需要存在。

Hence it is perfectly acceptable to leave library.jar out during runtime.

因此,离开图书馆是完全可以接受的。在运行时jar。

#7


0  

Yes.

是的。

Think about the following. If you add the following code to that library.jar class:

考虑以下。如果您向该库添加以下代码。罐类:

 public ShutDown {static { System.exit(-1); }}

That code won't be loaded automatically by the default system, because none of the existing code has a reference or knows about the ShutDown class, nor had a way to load it, and the Java class loader don't just go around trying to load random classes from the jar.

默认系统不会自动加载这些代码,因为所有现有代码都没有引用或不知道ShutDown类,也没有办法加载它,而且Java类装入器也不会到处尝试从jar加载随机类。

The way classes are loaded are described in the previous answers, if you review them carefully none of them include a "If there is a class in the jar it would be loaded" of some sort.

在前面的答案中描述了类的加载方式,如果您仔细检查它们,它们中没有一个包含“如果jar中有一个类会被加载”之类的。