在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中,而且最好根据语言规范对此类案例的说法。)
答案 0 :(得分:28)
如果正在执行对象方法,则表示某人拥有该引用。所以不,在执行方法时,对象无法进行GC。
答案 1 :(得分:2)
大多数情况下,垃圾收集是透明的。它可以消除手动内存管理的不必要的复杂性。所以,它似乎不会被收集,但实际发生的事情会更加微妙。
通常,编译器可能会完全忽略对象的构造。 (通过编译器,我的意思是比javac更低级别的编译器。字节码将是源的字面音译。)更加模糊的是,垃圾收集通常在单独的线程中运行,并且实际上删除未访问的对象,因为正在运行它的方法。
如何观察?终结者通常的嫌疑人。它可以与在对象上运行的方法同时运行。通常情况下,你会在终结器和普通方法中使用synchronized
块来解决这个问题,这会引入必要的发生在之前的关系。
答案 2 :(得分:2)
m只是一个存储引用的变量。程序员将使用它来进一步使用相同的对象在同一对象上编写逻辑。
执行时,程序将转换为OP-CODES / INSTRUCTIONS。 这些INSTRUCTION将具有对象的引用(毕竟它是一个内存位置)。 在存在m的情况下,将通过INDIRECT REFERENCE访问对象的位置。 如果m不存在,则参考是直接的。
所以这里,CPU寄存器正在使用对象,而不管使用引用变量。
这将一直有效,直到执行流程在main()函数的范围内。
此外,根据GC流程,一旦GC确定不再使用该对象,GC只会从内存中删除对象。
每个物体都有机会存活多次(取决于情况和算法)。一旦机会数量结束,则只有对象被垃圾收集。
简单来说,最近使用的对象将有机会留在记忆中。 旧对象将从内存中删除。
所以给你的代码:
public class Main {
public void run() {
// ...
}
public static void main(String[] args) {
new Main().run();
}
}
该对象不会被垃圾收集。
此外,例如,尝试查看匿名类示例。或者来自AWT / SWING中事件处理的示例。
在那里,你会发现很多这样的用法。
答案 3 :(得分:-1)
接受的答案不正确。对象是否可以被 GC 取决于您的 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)。
你可以看看this answer