Java中的局部变量清空对GC有帮助吗

发布于 2024-07-12 01:51:58 字数 697 浏览 8 评论 0原文

在离开方法之前,我“被迫”将 myLocalVar = null; 语句添加到finally子句中。 原因是为了帮助GC。 有人告诉我下次服务器崩溃时我会在晚上收到短信,所以我最好这样做:-)。

我认为这是毫无意义的,因为 myLocalVar 的范围仅限于方法,并且一旦方法退出就会“丢失”。 额外的空化只会污染代码,但在其他方面是无害的。

我的问题是,这个关于帮助 GC 的神话从何而来? (我被提到了“Java内存书籍”)你知道任何来自“权威”的文章更深入地解释它吗? 有没有可能这不是一个神话,而是确实有帮助? 如果是这样,怎么办? 将局部变量置空可能会造成任何损害吗?

为了澄清,方法如下所示:

void method() {
  MyClass myLocalVar = null;
  try {
    myLocalVar = get reference to object;
    ... do more here ...
  } finally {
    if (myLocalVar != null) {
      myLocalVar.close(); // it is resource which we should close
    }

    myLocalVar = null; // THIS IS THE LINE I AM TALKING ABOUT
  }
}

I was 'forced' to add myLocalVar = null; statement into finally clause just before leaving method. Reason is to help GC. I was told I will get SMS's during night when server crashes next time, so I better did it :-).

I think this is pointless, as myLocalVar is scoped to method, and will be 'lost' as soon as method exits. Extra nulling just pollutes the code, but is harmless otherwise.

My question is, where does this myth about helping GC come from? (I was referred to "Java memory books") Do you know any article from 'authorities' which explain it in more depth? Is there possibility this is not a myth, but really helps somehow? If so, how? May nulling local variables cause any harm?

To clarify, method look like this:

void method() {
  MyClass myLocalVar = null;
  try {
    myLocalVar = get reference to object;
    ... do more here ...
  } finally {
    if (myLocalVar != null) {
      myLocalVar.close(); // it is resource which we should close
    }

    myLocalVar = null; // THIS IS THE LINE I AM TALKING ABOUT
  }
}

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

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

发布评论

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

评论(15

金兰素衣 2024-07-19 01:51:58

有一个旧的 Sun 文档,Java Platform Performance(不幸的是,链接现已损坏,我还没有找到新的链接),其中描述了将超出范围的局部变量置空实际上会对 GC 产生影响的情况。

然而,该论文提到了java的一个非常旧的版本。 正如这个问题中提到的(其中还包含论文中描述的问题的概要),这不再影响当前的 JVM 实现。

There was an old piece of Sun documentation, Java Platform Performance (link sadly now broken, and I haven't been able to find a new one), which described a situation where nulling a local variable which dropped out of scope actually had an effect on the GC.

However, the paper referred to a very old version of java. As mentioned in this question (which also contains a précis of the problem described in the paper), this no longer affects current JVM implementations.

零時差 2024-07-19 01:51:58

Java GC 应该是“健全的”,但不一定立即“完整”。 换句话说,它的设计使得它永远不会消除仍然可以通过至少一条路径访问的对象(从而导致悬空引用)。 它不一定会立即完成,因为可能需要一些时间才能删除所有可以删除的内容。

我认为大多数 GC 神话都源于对这个概念的误解。
许多人保留了太多实例变量,这会导致问题,但这当然不是这里的问题。

其他人将局部变量放在实例变量中(例如,通过将其传递给函数),然后认为使局部变量无效会以某种方式消除该变量,这当然是不正确的。

最后,有些人过度依赖 GC,并认为它会为他们执行功能关闭(例如,当变量被删除时关闭连接),当然事实并非如此。 我认为这句话的根源是“我真的已经完成了,但我不知道如何确保这一点”。

所以,是的,你是对的,这是不必要的。

The Java GC is supposed to be "sound" but is not necessarily immediately "complete". In other words, it is designed so that it would never eliminate objects that are still accessible by at least one path (and thus cause a dangling reference). It is not necessarily immediately complete since it might take time until it removes everything that can be removed.

I think that most GC myths come from a misunderstanding of that concept.
Many people keep too many instance variables alive, and that causes problems, but that is of course not the issue here.

Other people put the local variables in an instance variable (e.g., by passing it to function), and then think that nullifying the local variable somehow eliminates the variable, which is of course untrue.

Finally, there are people who overrely on the GC and think it would do functional shutdown for them (E.g., close connections when variable is removed) which is of course not the case. I think the source of this line is the "I'm really really done with it but I'm not sure how to ensure that".

So yeah, you're correct that it's unneccessary.

风柔一江水 2024-07-19 01:51:58

不是在这种情况下。 一旦函数返回,myLocalVar 就会超出范围,因此将引用设置为 null 绝对不会执行任何操作。

Not in this case. myLocalVar falls out of scope as soon as the function returns, so setting the reference to null does absolutely nothing.

温柔女人霸气范 2024-07-19 01:51:58

这是一个神话,可以追溯到 java 刚出现时,C++ 人员不信任 gc。

gc 知道它在做什么。 清空 var 不会造成任何伤害,但也不会真正有任何帮助。 就在前几天,杰夫对此发表了一篇非常有趣的帖子

That is a myth that goes way back to when java first came out and C++ guys didn't trust the gc.

The gc knows what it is doing. nulling out var wont hurt anything, but it wont really help anything either. Jeff had a pretty funny post on this just the other day.

独夜无伴 2024-07-19 01:51:58

据我所知,在变量离开作用域之前将其清空对于垃圾收集器没有任何影响。

当然,在某些情况下它确实有帮助。 例如,当 var 不是局部变量而是成员或静态成员时。 然后销毁引用可能会使对象无法访问,从而符合收集条件。

另一种情况是,如果函数分配大量临时内存来初始化一些数据以供进一步处理,并且可以在开始处理之前丢弃对临时内存的所有引用,那么即使对于局部变量也可能有所帮助:

SomeResult SomeFunction(SomeClass param) {
    TempData big = new TempData(param);
    IntermediateResult intermediate = big.GetIntermediateResult();
    big = null; // allow GC to reclaim the memory before returning from the function
    intermediate.FurtherProcessing();
    return intermediate.EvenMoreProcessing();
}

To the best of my knowledge, nulling a variable immediately before it leaves the scope makes no difference to the garbage collector.

Of course there are cases where it indeed does help. E.g. when var is not a local variable but a member or static member. Then destroying the reference might make the object unreachable and thus eligible for collection.

Another case where it might help even with local variables if a function allocates much temporary memory to initialise some data for further processing and can throw away all references to the temporary memory before beginning the processing:

SomeResult SomeFunction(SomeClass param) {
    TempData big = new TempData(param);
    IntermediateResult intermediate = big.GetIntermediateResult();
    big = null; // allow GC to reclaim the memory before returning from the function
    intermediate.FurtherProcessing();
    return intermediate.EvenMoreProcessing();
}
瞳孔里扚悲伤 2024-07-19 01:51:58

清空局部变量确实可以在某些边缘情况下有所帮助。 这不适用于原始问题中的情况,但无论如何都是有教育意义的...让我们考虑这个程序:

public class Main {
    public static void main(String[] args) {
       {
           Main local = new Main();

           // inner = null;
       }

       while (true) {
           // long running method
       }
    }
}

如果 inner = null; 被注释掉,则 local 变量中的对象while 循环期间无法进行垃圾收集。 原因是 Java 虚拟机不知道这样的范围。 它所拥有的只是:

D:\workspaces\workspace-3.4\test\src>javap -verbose -c Main
public class Main extends java.lang.Object
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #4.#11; //  java/lang/Object."<init>":()V
const #2 = class        #12;    //  Main
const #3 = Method       #2.#11; //  Main."<init>":()V
const #4 = class        #13;    //  java/lang/Object
const #5 = Asciz        <init>;
const #6 = Asciz        ()V;
const #7 = Asciz        Code;
const #8 = Asciz        main;
const #9 = Asciz        ([Ljava/lang/String;)V;
const #10 = Asciz       StackMapTable;
const #11 = NameAndType #5:#6;//  "<init>":()V
const #12 = Asciz       Main;
const #13 = Asciz       java/lang/Object;

{
public Main();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   new     #2; //class Main
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   goto    8
  StackMapTable: number_of_entries = 1
   frame_type = 8 /* same */

}

没有有关局部变量范围的信息。 所以从 JVM 的角度来看,上面的程序相当于:(

public class Main
{

    public Main() { }

    public static void main(String args[])
    {
        Main main1 = new Main();
        do
            ;
        while(true);
    }
}

由 JAD 反编译器生成)

结论:在像这样的非常特殊的情况下,将局部变量清空是有一定道理的。 但如果方法很快就会完成(就像我原来的问题一样),那就没有帮助。

这是受到评论的启发 Zdenek Tronicek 在 java-cz 邮件列表(捷克语,抱歉)

Nulling local variables can indeed help in some edge cases. This doesn't apply to situation in original question, but is educational anyway... Let's consider this program:

public class Main {
    public static void main(String[] args) {
       {
           Main local = new Main();

           // inner = null;
       }

       while (true) {
           // long running method
       }
    }
}

If inner = null; is commented out, object in local variable cannot be garbage-collected during while loop. Reason is that Java Virtual Machine doesn't know about scopes like this. All it has is:

D:\workspaces\workspace-3.4\test\src>javap -verbose -c Main
public class Main extends java.lang.Object
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #4.#11; //  java/lang/Object."<init>":()V
const #2 = class        #12;    //  Main
const #3 = Method       #2.#11; //  Main."<init>":()V
const #4 = class        #13;    //  java/lang/Object
const #5 = Asciz        <init>;
const #6 = Asciz        ()V;
const #7 = Asciz        Code;
const #8 = Asciz        main;
const #9 = Asciz        ([Ljava/lang/String;)V;
const #10 = Asciz       StackMapTable;
const #11 = NameAndType #5:#6;//  "<init>":()V
const #12 = Asciz       Main;
const #13 = Asciz       java/lang/Object;

{
public Main();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=2, Args_size=1
   0:   new     #2; //class Main
   3:   dup
   4:   invokespecial   #3; //Method "<init>":()V
   7:   astore_1
   8:   goto    8
  StackMapTable: number_of_entries = 1
   frame_type = 8 /* same */

}

There is no information about scope of local variable. So from JVM's point of view, above program is equivalent to:

public class Main
{

    public Main() { }

    public static void main(String args[])
    {
        Main main1 = new Main();
        do
            ;
        while(true);
    }
}

(Generated by JAD decompiler)

Conclusion: there IS some rationale in nulling local variables in very special cases like this. But if method is going to finish soon (like in my original question), it doesn't help.

This was inspired by comment from Zdenek Tronicek on java-cz mailing list (in czech language, sorry)

╄→承喏 2024-07-19 01:51:58

你是对的。 清空一个无论如何都会立即超出范围的变量是不必要的,并且对 GC 没有任何影响。 它所做的只是使代码变得混乱。 在Effective Java 2nd Edition中,作者建议不要对局部变量进行不必要的清空。 请参阅《Effective Java》,第 2 版,第 6 项:消除过时的对象引用,以获得完整的文章。

您还可以在文章 创建和销毁 Java 对象< /a>,在 InformIT。 阅读整篇文章,找出 Joshua Bloch 同意您观点的地方。

当局部变量超出范围时,与取消对它的引用完全相同。

编辑:在Sun 网站上添加Effective Java 2nd Edition 的链接

You are correct. Nulling out a variable that will immediately fall out of scope anyway is unnecessary and makes no difference whatsoever to GC. All it does is clutter the code. In Effective Java 2nd Edition, the author recommends against unnecessary nulling out of local variables. See Effective Java, 2nd Edition, Item 6: Eliminate obsolete object references, for a full writeup.

You can also see this in the article Creating and Destroying Java Objects, at InformIT. Read the entire article to find the place where Joshua Bloch agrees with you.

When a local variable falls out of scope, it is exactly the same as if you null the reference to it.

EDIT: Add link to Effective Java 2nd Edition at Sun website

零度℉ 2024-07-19 01:51:58

正如您正确指出的那样,在这种情况下取​​消是完全没有意义的。

回到 JDK1.3,我确实遇到过一个带有非常大的对象图的情况,该图内还包含大量循环引用。 清除一些引用实际上确实相当明显地缩短了 GC 时间。

我不确定这是否适用于现代虚拟机。 垃圾收集者变得越来越聪明。

As you correctly point out, nulling out in this case is totally pointless.

Back on JDK1.3 I did actually have a case with an extremely large object graph that also contained lots of circular references within the graph. Clearing out a few of the references did actually improve GC times quite noticeably.

I am not sure if this would apply with a modern VM. Garbage collectors have become increasingly smarter.

动听の歌 2024-07-19 01:51:58

这个神话中的另一个可能的因素是,如果您在方法结束之前完成了局部变量的清零,那么它可能会产生影响。 这将允许 GC 在方法完成之前收集该对象,这可能很有用。

有人可能在某个时候收到过这样的建议,并将其误解为需要始终清空局部变量。

Another possible factor in this myth is that it can make a difference to null out a local variable if you are done with it before the end of the method. This would allow the GC to collect that object before the method is complete, which could be useful.

Someone might have been given that advice at some point and misunderstood it as a need to always null out the local variables.

一绘本一梦想 2024-07-19 01:51:58

我发现只有两种情况下将变量设置为 null 很有用:

  • 在在字段中创建大对象的单元测试中。 单元测试人员可以在所有测试的生命周期中保留测试对象和您创建的对象。 这可能会导致测试仪内存不足。 在这种情况下,通常最好使用局部变量而不是字段,但如果需要字段,则可以在拆卸中将其清除。
  • 循环引用可以通过 GC 来清理,但不能通过简单的增量收集来清理。 这可能意味着具有循环引用的对象需要更多的工作来清除,并且比其他对象的寿命更长。 一般来说,这并不重要,但如果您想减少完整的 GC 时间,它可以帮助打破循环引用。

There are only two cases where I have found setting a variable to null has been useful:

  • In unit tests which create a large object in a field. The unit tester may retain the test object and the objects you created for the life of all the tests. This can cause the tester to run out of memory. In this case it is often better to use local variables instead of fields, but if a field required it can be cleared in the tearDown.
  • Circular references can be cleaned up by the GC but not with a simple incremental collection. This can mean that objects with circular references take more work to clear and can live much long than they would otherwise. Generally this doesn't matter but if you are trying to reduce your full GC time, it can help to break circular references.
静谧 2024-07-19 01:51:58

我不知道技术细节,但据我所知,该变量只不过是当前堆栈帧的引用,并且在删除该引用之前,该对象无法被垃圾收集。 现在,但明确地将其设置为 null,您已确保引用已消失。 如果你不这样做,你基本上是让虚拟机决定何时清除这个引用,这可能会也可能不会在退出作用域时发生(与 C++ 不同,如果对象位于堆栈上并且必须被销毁)。 可能是当堆栈帧被下一个堆栈帧覆盖时。 我不确定是否真的有一个虚拟机可以做到这一点。

简短的回答是,这是不必要的,标记和扫描最终会得到它。 顶多是个时间问题。

I don't know the technical details, but as far as I can remember, the variable is nothing more than reference from the current stack frame, and until this reference is removed, the object cannot be garbage collected. Now, but explicitly setting it to null, you've made sure that the reference is gone. If you don't you're basically letting the VM decide when this reference is cleared, which might or might not be upon exiting the scope (unlike C++, if the object is located on the stack and MUST be destroyed). It might be when the stack frame is overwritten with the next. I'm not sure if there's actually a VM which does this.

Short answer though, it's unnecessary, the mark and sweep will get it eventually. It's at most a question of time.

寂寞陪衬 2024-07-19 01:51:58

在某些情况下,空变量(通常是实例变量或类变量)可能很有用。 但是在方法结束之前立即将本地变量清空绝对没有任何作用。

当您将变量设置为 null 时,您只是删除了对实际对象的引用。 但是当局部变量超出范围时,引用无论如何都会被删除; 因此,将其设置为 null 作为该方法的最后一行是多余的。

In some circumstances, it can be useful to null variables (usually instance or class variables). But nulling a local variable immediately before the end of the method does absolutely nothing.

When you set a variable to null, you are merely removing that reference to the actual object. But when a local variable goes out of scope, the reference is removed anyway; therefore, setting it to null as the last line of the method is simply redundant.

阳光下的泡沫是彩色的 2024-07-19 01:51:58

如果您的类挂起很长时间,那么将其引用的对象清空将允许它们被收集。

这几乎从来都不是问题,大多数时候清空对象是没有用的。

当您考虑对象分配和释放时,请注意“系统”处理的事物:活动线程、尚未 dispose()d 的窗口,以及一两个以上但我记不清的东西现在。

系统中的每个对象都“悬挂”在巨大的倒置树中的这些安装点上。 如果你从这些“根”上砍掉任何一根树枝,整个树枝就会掉到地上,并被垃圾收集割草机收集起来。

大多数类在其整个生命周期中都需要它们的所有成员变量——当它们的生命周期结束时,它们的整个分支将被修剪,包括它们的所有成员; 因此 - 不需要为空。

(顺便说一下,这些修剪非常有效,甚至比 C++ 的免费修剪还要高效,因为它们在释放时不需要接触每个对象)

If your class hangs around for a long time, then nulling out objects it references will allow them to be collected.

This is almost never an issue, most times nulling objects is not useful.

When you think of object allocation and freeing, pay attention to the things that the "System" has handles to: Active threads, windows that haven't been dispose()d, and one or two more things but I can't remember right now.

Every object in your system "Hangs" off these mount points in a giant upside-down tree. If you cut any branch free from these "Roots" the entire branch falls to the ground and is collected by the Lawn Mower of Garbage Collecting.

Most classes need all their member variables for their entire lifecycle--and when their life is finished, their entire branch is trimmed including all their members; hence--no need to null.

(these trims, by the way, are quite efficient, even more than C++'s free since they don't require touching each object as it's freed)

世俗缘 2024-07-19 01:51:58

如果你的本地作用域中不再需要大对象,你可以给 JVM 一个提示并将引用设置为 NULL。

public void foobar()
{
    List<SomeObject> dataList = new ArrayList<SomeObject>();

    // heavy computation here where objects are added to dataList 
    // and the list grows, maybe you will sort the list and
    // you just need the first element... 

    SomeObject smallest = dataList.get(0);

    // more heavy computation will follow, where dataList is never used again
    // so give the JVM a hint to drop it on its on discretion
    dataList = null;

    // ok, do your stuff other heavy stuff here... maybe you even need the 
    // memory that was occupied by dataList before... 

    // end of game and the JVM will take care of everything, no hints needed
}

但在返回之前没有任何意义,因为这是由 JVM 自动完成的。 所以我同意之前所有的帖子。

If you don't need large objects in your local scope anymore, you can give the JVM a hint and set the reference NULL.

public void foobar()
{
    List<SomeObject> dataList = new ArrayList<SomeObject>();

    // heavy computation here where objects are added to dataList 
    // and the list grows, maybe you will sort the list and
    // you just need the first element... 

    SomeObject smallest = dataList.get(0);

    // more heavy computation will follow, where dataList is never used again
    // so give the JVM a hint to drop it on its on discretion
    dataList = null;

    // ok, do your stuff other heavy stuff here... maybe you even need the 
    // memory that was occupied by dataList before... 

    // end of game and the JVM will take care of everything, no hints needed
}

But it does not make sense before the return, because this is done by the JVM automatically. So I agree with all postings before.

暮光沉寂 2024-07-19 01:51:58

就 GC 而言,将局部变量置空不仅毫无意义,而且可能会导致不必要地将变量加载到寄存器中以将其置空,这会使情况变得更糟。 想象一下,如果上次读取或写入 myLocalVar 之间有 1000 行代码,然后您引用它只是为了清空引用。 该值早已从寄存器中消失,但您必须将其加载回内存才能使用它。

Not only is nulling a local variable like that meaningless in terms of GC, it may cause unnecessary loading of the variable into a register in order to null it out, which makes the situation worse. Imagine if there is 1000 lines of code between the last read or write to myLocalVar, and then you reference it just to null out the reference. The value is long gone from the register, but you have to load it back in to memory to work with it.

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