有没有什么方法可以多次执行静态块?如果是的话怎么样?

时间:2010-05-20 06:39:10

标签: java classloader

我的理解 在类加载期间执行静态块,如果已经加载了类,则除了类重新加载之外无法加载类

怀疑/问题 1)JVM是否有时间重新加载课程?

我的理解 在类加载中JVM加载Java文件的字节码,因此它不能将所有数千个类字节码保存在内存中,因此它可能会丢弃很少使用的代码并在必要时重新加载它并且在重新加载期间JVM没有初始化静态变量和再次阻塞(可能正在使用一些跟踪机制)

怀疑/问题
2)如果我的上述理解不正确,请纠正我

6 个答案:

答案 0 :(得分:6)

据我所知,JVM永远不会重新加载一个类本身;一旦加载一个类,它就会永远保持加载状态。因此,类定义保存在“PermGen”内存池中。

但是,您的类的字节码可能由多个类加载器加载,每次发生这种情况时,静态块将再次执行,因为这是一个新类。每个类只在其自己的类加载器的范围内可见,而通常任何类加载器都可以看到你的字节码,如果它在类路径上,所以这是一种可能的(如果不希望的话)。

答案 1 :(得分:3)

如果ClassLoader加载类无法访问,则可以卸载类:https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.7

  

当且仅当其定义的类加载器可以由垃圾收集器回收时,可以卸载类或接口,如第12.6节中所述。

然后,如果该类需要再次使用,它显然会再次加载。事实上,几个类加载器可以单独加载相同的类。

答案 2 :(得分:2)

当加载类时,每个类加载器执行一次静态块和一次ONCE。 按照其出现的静态块执行顺序,请参阅下面的示例代码及其输出。 静态代码是在类级别而不是实例级别,每个ClassLoader实例化此类。 注意:为简洁起见,我没有在这里调用方法及其输出。

public class StaticTest {

 // 1st Static block invoked first.
static{
    System.out.println("hello...1");
}

// 2nd Static block, invoked after 1st static block above.
static{
    System.out.println("hello...2");
}


public static void Staticmeth() {
     System.out.println("hello...3");
}

public static void main(String ag[]){

}

}

节目输出: -

喂... 1

您好... 2

答案 3 :(得分:1)

我明确地使用新的ClassLoader再次加载该类,此类的静态块将再次执行。

答案 4 :(得分:0)

Java语言规范详细说明了加载,卸载和重新加载类的机制。

JLS 12.2 Loading of Classes and Interfaces

  

正在加载是指查找具有特定名称的classinterface类型的二进制形式的过程,可能是通过计算它在运行中,但更典型的是通过检索先前由编译器从源代码计算的二进制表示,并从该二进制形式构造Class对象来表示类或接口。

     

加载的精确语义在Java虚拟机规范的第5章中给出(每当我们在本书中引用Java虚拟机规范时,我们指的是第二版,由JSR 924修改)。在这里,我们从Java编程语言的角度概述了该过程。

     

类或接口的二进制格式通常是上面引用的Java虚拟机规范中描述的类文件格式,但只要满足第13.1节中规定的要求,其他格式也是可能的。 defineClass的{​​{1}}方法可用于从类文件格式的二进制表示中构造class ClassLoader个对象。

在某些情况下,可以卸载类和接口,这可能会导致不可避免的重新加载。

12.7 Unloading of Classes and Interfaces

  

Java编程语言的实现可以卸载类。当且仅当其定义的类加载器可以由垃圾收集器回收时,可以卸载类或接口,如第12.6节中所述。可能无法卸载由引导加载程序加载的类和接口。

     

类卸载是一种有助于减少内存使用的优化。 显然,程序的语义不应该取决于系统是否以及如何选择实现诸如类卸载之类的优化。否则会损害程序的可移植性。因此,无论是否已卸载类或接口,都应对程序透明。

     

但是,如果在定义加载器可能可访问的情况下卸载了类或接口C,则可能会重新加载C.人们永远无法确保不会发生这种情况。

事实上,它解决了您的具体问题:

  

例如,如果班级有:

,则重新加载可能不透明      
      
  • 静态变量(其状态将丢失)。
  •   
  • 静态初始值设定项(可能有副作用)。
  •   
  • 本机方法(可能保留静态)。
  •   
     

此外,Class对象的哈希值取决于其标识。因此,通常无法以完全透明的方式重新加载类或接口。

     

由于我们永远无法保证卸载其加载器可能可访问的类或接口不会导致重新加载,并且重新加载永远不会透明,但卸载必须是透明的,因此必须在卸载时不要卸载类或接口是可以到达的。可以使用类似的推理线来推断出由引导加载程序加载的类和接口永远不会被卸载。

     

如果可以回收定义的类加载器,还必须说明为什么卸载C类是安全的。如果可以回收定义的加载器,那么永远不会有任何对它的实时引用(这包括不是实时的引用,但可能由终结器复活)。反过来,只有在不能对该加载器定义的任何类进行任何实时引用时才能这样,包括C,无论是来自它们的实例还是来自代码。

     

类卸载是一种优化,仅对于加载大量类并且在一段时间后停止使用大多数类的应用程序才有意义。这种应用程序的一个主要示例是Web浏览器,但还有其他一些。这类应用程序的一个特征是它们通过显式使用类加载器来管理类。因此,上述政策适用于他们。

     

严格地说,本规范讨论类卸载问题并不重要,因为类卸载仅仅是一种优化。然而,这个问题非常微妙,因此在此通过澄清提及。

答案 5 :(得分:0)

Andrzej Doyle回答了你的具体问题。但是:

  

我的理解静态块是   在课程加载期间执行

要清楚,加载初始化是不同的阶段;可以在不初始化的情况下加载类。 Java Virtual Machine Specification指定将导致类初始化的三个条件:创建实例,调用静态方法或使用或分配非常量静态字段。