在Java中,我做了下面的事情,没有考虑太多:
public class Main {
public void run() {
// ...
}
public static void main(String[] args) {
new Main().run();
}
}
然而,最近我变得不确定这样做是否安全。毕竟,在创建Main
对象之后没有对它的引用(好吧,有this
引用,但这算数吗?),所以看起来存在垃圾收集器可能在执行某些内容时删除对象的危险。所以也许main
方法应该是这样的:
public static void main(String[] args) {
Main m = new Main();
m.run();
}
现在,我很确定第一个版本可以工作,我从来没有遇到过任何问题,但我想知道它是否在所有情况下都是安全的(不仅在特定的JVM中,而且最好根据语言规范对这些情况的说明)。
如果一个对象方法正在被执行,这意味着有人拥有该引用。所以,当一个方法正在执行时,对象不能被GC。
大多数情况下,垃圾收集是透明的。它的存在是为了消除手动内存管理的不必要的复杂性。因此,它看起来没有被收集,但实际发生的情况更微妙。
很简单,编译器可以完全省略对象的构造。(这里的编译器是指比javac低级的编译器。字节码将是源代码的字面音译。)更令人费解的是,垃圾收集通常在单独的线程中运行,并且实际上将未访问的对象删除为正在运行的方法。
这是如何观察到的?决赛中常见的疑点。它可以与对象上运行的方法并发运行。通常情况下,您可以在终结器和正常方法中使用synchronized
块来解决这个问题,这引入了必要的happens-before关系。
m只是一个存储了引用的变量。这将被程序员用来使用同一个对象来进一步在同一个对象上编写逻辑。
执行时,程序将转换为操作码/指令。这些指令将有对象的引用(毕竟它是一个内存位置)。在m存在的情况下,对象的位置将通过间接引用访问。如果m不存在,则引用为DIRECT。
所以在这里,对象被CPU寄存器使用,与引用变量的使用无关。
直到执行流在main()函数的作用域中才可用。
此外,根据GC进程,只有当GC确定对象不会再被使用时,才会从内存中删除对象。
每个对象都有机会存活多次(取决于情况和算法)。一旦次数结束,那么只有对象被垃圾收集。
简单来说,最近使用过的对象将有机会留在内存中。旧的对象将从内存中删除。
给定你的代码:
public class Main {
public void run() {
// ...
}
public static void main(String[] args) {
new Main().run();
}
}
对象将不会被垃圾收集。
同样,举例来说,试着看看匿名类的例子。或者AWT/SWING中的事件处理示例。
在那里,你会发现很多这样的用法。
接受的答案不正确。对象是否可以被GCed取决于你的public void run() {// ...}
方法是否有对类实例(this)的引用。试一试:
public class FinalizeThis {
private String a = "a";
protected void finalize() {
System.out.println("finalized!");
}
void loop() {
System.out.println("loop() called");
for (int i = 0; i < 1_000_000_000; i++) {
if (i % 1_000_000 == 0)
System.gc();
}
// System.out.println(a);
System.out.println("loop() returns");
}
public static void main(String[] args) {
new FinalizeThis().loop();
}
}
以上程序总是输出
loop() called
finalized!
loop() returns
在Java 8中。但是,如果取消对System.out.println(a)
的注释,输出将变为
loop() called
a
loop() returns
这次没有GC,因为调用的方法引用了实例变量this.a。
你可以看看这个答案