java system.gc()仅当我隐式设置obj = null时才有效?

发布于 2025-02-06 12:29:53 字数 1192 浏览 6 评论 0原文

我在Windows 10上使用Java 8,并且我有此代码段来测试System.gc()的工作方式:

public class testGc{
    static class MyObject{
        @Override
        protected void finalize() throws Throwable{ // not called when System.gc()
            System.out.println("gc: " + toString());
        }
    }

    public static void main(String[] args) throws Exception{
        {
            MyObject obj=new MyObject();
            System.out.println("NOT set to null");
        }
        System.gc(); // obj still exists
        Thread.sleep(500);
    }
}

您可以在我的main()中看到,代码块结束,而OBJ不再被引用,然后是System.gc.gc ()。我希望这种“ OBJ”被回收。但是此程序仅打印:

NOT set to null
public static void main(String[] args) throws Exception{
    {
        MyObject obj=new MyObject();
        System.out.println("Set to null");
        obj=null;//need this expression!
    }
    System.gc(); // now it works
    Thread.sleep(500);
}

现在程序打印:

Set to null
gc: testGc$MyObject@2130772

为什么我需要使用“ = null”来使System.gc()工作并调用我的“最终化”函数?在代码块之后,“ OBJ”不再有效。

如何理解这一点?

I'm using Java 8 on windows 10 and I have this code snippet to test how System.gc() works:

public class testGc{
    static class MyObject{
        @Override
        protected void finalize() throws Throwable{ // not called when System.gc()
            System.out.println("gc: " + toString());
        }
    }

    public static void main(String[] args) throws Exception{
        {
            MyObject obj=new MyObject();
            System.out.println("NOT set to null");
        }
        System.gc(); // obj still exists
        Thread.sleep(500);
    }
}

You can see in my main(), the code block ends and obj is no longer referenced, and then System.gc(). I expect that this "obj" is recycled. But this program only prints:

NOT set to null
public static void main(String[] args) throws Exception{
    {
        MyObject obj=new MyObject();
        System.out.println("Set to null");
        obj=null;//need this expression!
    }
    System.gc(); // now it works
    Thread.sleep(500);
}

Now the program prints:

Set to null
gc: testGc$MyObject@2130772

Why do I need to use "=null" to make System.gc() work and call my "finalize" function? The "obj" is no longer valid after the code block.

How to understand this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

墨洒年华 2025-02-13 12:29:53

因为那不是JVM的工作方式。

局部变量,在JVM(类文件)级别,不存在。取而代之的是堆栈(类文件是基于堆栈的语言),任何方法都设置了固定数量的“插槽”,这些方法未命名。局部变量和插槽之间只有一个光“链接”。例如,此代码:

void example() {
 int a = 5;
 System.out.println(a);
 int b = 10;
 System.out.println(b);
}

最终将仅使用一个插槽(甚至可能没有任何插槽,而将文字直接推向堆栈,然后println方法将消耗它们)。

仍无法收集任何仍在主动方法中的本地“插槽”“指向”的对象。老虎机不会消失。因此,在您的示例代码中,是的,在Java(语言)术语中obj变量不再存在,而是在Java(the Virtual Machine)enter < /strong>,它确实 - 该方法具有1个插槽,第一个插槽指向该对象。 Javac不会决定添加其他代码来撤消此事实以帮助垃圾收集 - 数十亿个记忆写入这将几乎完全没有用,并且会使收益倍增!

取而代之的是,当方法退出时,其堆栈空间和这些插槽都被重复使用(并且不再以明确地写在它们上的更聪明的方式来防止垃圾收集),并且对于几乎所有的意图和目的而言,这已经足够快地了。

这确实意味着在非常罕见的情况下,

void example() {
  {
    List<String> list = ... make some GIGANTIC 1GB+ list ...
    process(list);
  }

  callSomeMethodThatAlsoNeedsTonsOfMemory();
}

您将遇到内存错误,并且明智的做法是在中间添加一个显式list = null;。除了实际上不是事实 - 更好的是将“块”按照上面的书面写,然后将其粘贴在一种方法中,然后仅用2个调用替换此方法。现在无需这样做 - 包含1GB+列表的方法(和process(list)调用)在callomememethodthatalsoneedstonsofmemory开始之前结束,这一事实意味着其插槽是正常清除的。

再进一步上升 - 列出巨大的列表然后对其进行处理可能是不明智的,然后立即垃圾收集。很少有算法有意义 - 您很可能想处理此数据单位单位(而不是生成100万个条目,将它们粘在列表中,然后在列表中处理列表 - 在循环中生成1个条目,对其进行处理,然后生成下一个,处理该处理,等等),或者,如果需要1GB,则如果您的扩展更高,则可能需要100GB,因此需要使用磁盘支持的数据库解决方案。

换句话说,完全有意的是,在这些异国情调的情况下,JVM可以在可能的情况下进行GC。

nb:system.gc()只是一个请求;一种暗示。它不能保证垃圾收集实际上会发生。但是,它通常“起作用”,并且对您目睹的内容的解释与system.gc不是保证的事实无关。

Because that's not how the JVM works.

local variables, at the JVM (class file) level, do not exist. Instead there's the stack (class files are a stack based language), and any method sets up a fixed amount of 'slots', that are unnamed. There is only a light 'link' between local variables and slots. For example, this code:

void example() {
 int a = 5;
 System.out.println(a);
 int b = 10;
 System.out.println(b);
}

will end up using only a single slot (or probably even no slots whatsoever, with the literals pushed straight onto the stack, and the println method will then consume them).

Any object that is still 'pointed at' by a local 'slot' in an active method cannot be garbage collected. Slots don't disappear. So in your example code, yes, in java (the language) terms, The obj variable no longer exists, but in java (the virtual machine) terms, it does - that method has 1 slot, and the first slot is pointing at that object. javac is not going to decide to add additional code just to undo this fact to aid garbage collection - the billions of memory writes this would entail would be almost entirely useless and would dwarf the gains!

Instead when a method exits, its stack space and those slots all get reused (and marked away as no longer preventing garbage collection in a much smarter fashion that explicitly writing over them), and that's 'fast enough' for pretty much all intents and purposes.

This does mean that in extremely rare cases where you do something like this:

void example() {
  {
    List<String> list = ... make some GIGANTIC 1GB+ list ...
    process(list);
  }

  callSomeMethodThatAlsoNeedsTonsOfMemory();
}

you're going to run into memory errors, and it is wise to add an explicit list = null; in the middle. Except, that's not actually true - what's much better is to take the 'block' as written above and stick that in a method, then replace this method with just 2 calls. Now there's no need for that - the fact that the method containing the 1GB+ list (and the process(list) call) ends before callSomeMethodThatAlsoNeedsTonsOfMemory begins means its slots are cleared as normal.

And to go up even further - it's probably unwise to make a gigantic list and then process it, just to garbage collect it immediately afterwards. There are frightfully few algorithms where that makes sense - most likely you either wanted to process this data unit-by-unit (instead of generating 1 million entries, sticking them in a list, then processing the list - in a loop generate 1 entry, process it, then generate the next, process that, and so on), or, hey if it takes 1GB it will probably take 100GB if you scale a bit more, so this needs a disk-backed database solution instead.

In other words, it is entirely intentional that the JVM can in these exotic circumstances fail to GC when it could have.

NB: System.gc() is merely a request; a hint of sorts. It doesn't guarantee that garbage collection will actually occur. However, it usually 'works', and the explanation for what you are witnessing has nothing to do with the fact that System.gc isn't a guarantee.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文