19

这个问题尤其适用于 java 语言。我知道为所有静态代码预留了一个静态内存部分。

我的问题是这个静态内存是如何填充的?静态对象是在导入时还是在第一次引用时放入静态内存中的?此外,对于静态对象是否适用与所有其他对象相同的垃圾收集规则?


public class Example{
    public static SomeObject someO = new SomeObject();
}
/********************************/
// Is the static object put into static memory at this point?
import somepackage.Example;

public class MainApp{
    public static void main( Sting args[] ){
// Or is the static object put into memory at first reference?
       Example.someO.someMethod();
// Do the same garbage collection rules apply to a 
//     static object as they do all others?
       Example.someO = null;
       System.gc();
    }
}
4

6 回答 6

34

导入与编译代码中的任何指令无关。他们建立别名仅在编译时使用。

有一些反射方法允许类被加载但尚未初始化,但在大多数情况下,您可以假设每当一个类被引用时,它已经被初始化。

静态成员初始化器和静态块的执行就像它们在源代码顺序中都是一个静态初始化器块一样。

通过静态成员变量引用的对象被强引用,直到类被卸载。一个普通的ClassLoader永远不会卸载一个类,但应用服务器使用的那些在正确的条件下会这样做。然而,这是一个棘手的领域,并且是许多难以诊断的内存泄漏的根源——这也是不使用全局变量的另一个原因。


作为(切线)奖励,这是一个需要考虑的棘手问题:

public class Foo {
  private static Foo instance = new Foo();
  private static final int DELTA = 6;
  private static int BASE = 7;
  private int x;
  private Foo() {
    x = BASE + DELTA;
  }
  public static void main(String... argv) {
    System.out.println(Foo.instance.x);
  }
}

这段代码会打印什么?试试看,你会看到它打印出“6”。这里有几件事在起作用,其中之一是静态初始化的顺序。代码的执行就像是这样写的:

public class Foo {
  private static Foo instance;
  private static final int DELTA = 6;
  private static int BASE;
  static {
    instance = null;
    BASE = 0;
    instance = new Foo(); /* BASE is 0 when instance.x is computed. */
    BASE = 7;
  }
  private int x;
  private Foo() {
    x = BASE + 6; /* "6" is inlined, because it's a constant. */
  }
}
于 2009-01-01T20:04:24.657 回答
5

通常没有“静态”内存之类的东西。大多数虚拟机都有永久生成的堆(加载类的地方),通常不会被垃圾收集。

静态对象的分配与任何其他对象一样。但是,如果它们存活很长时间,它们将在垃圾收集器中的不同代之间移动。但它们不会最终进入永久空间。

如果你的类永久持有这个对象,它只会在 vm 退出时被释放。

于 2009-01-01T19:50:46.027 回答
3

一旦在代码中引用了您的类,这个静态变量some0 就会被初始化。在您的示例中,这将在您的 main 方法的第一行中执行。

您可以通过创建静态初始化程序块来验证这一点。在此初始化程序块中放置一个断点,您将看到何时调用它。或者更简单...在 SomeObject 的构造函数中放置一个断点。

于 2009-01-01T19:50:53.530 回答
3

静态变量的初始化在 suns JVM 规范的第2.11 节 Static Initializers中介绍。该规范没有定义垃圾收集的实现,但是我想静态对象的垃圾收集规则会因您的虚拟机而异。

于 2009-01-01T20:01:37.423 回答
2

应该注意的是,只有指针(或任何其他原始类型)存储在 PermGenSpace中(这是存储静态内容的区域的正确名称)。

因此,指针引用的对象与任何其他对象一样位于普通堆中。

于 2009-01-02T00:16:59.810 回答
0

如果更改静态字段以引用不同的对象,则静态字段指向的原始对象与任何其他对象一样有资格进行 GC。

如果类本身被卸载并且整个对象图从堆中删除,它也可以被释放(即使没有为空)。当然,什么时候可以卸载一个类对于许多其他问题来说是一个很好的话题...... :)

于 2009-01-02T04:13:26.917 回答