Java 中将对象赋值为 null 会影响垃圾回收吗?

发布于 2024-07-12 02:51:10 字数 165 浏览 4 评论 0 原文

在 Java 中将未使用的对象引用分配给 null 是否可以以任何可衡量的方式改进垃圾收集过程?

我使用 Java(和 C#)的经验告诉我,尝试智胜虚拟机或 JIT 编译器通常是违反直觉的,但我见过同事使用这种方法,我很好奇这是否是一个好的做法或者那些巫术编程迷信之一?

Does assigning an unused object reference to null in Java improve the garbage collection process in any measurable way?

My experience with Java (and C#) has taught me that is often counter intuitive to try and outsmart the virtual machine or JIT compiler, but I've seen co-workers use this method and I am curious if this is a good practice to pick up or one of those voodoo programming superstitions?

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

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

发布评论

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

评论(15

半城柳色半声笛 2024-07-19 02:51:10

通常情况下,不会。

但就像所有事情一样:这取决于情况。 如今,Java 中的 GC 非常好,所有内容在无法再访问后都应该很快被清理掉。 这是在为局部变量留下方法之后,并且不再为字段引用类实例时。

如果您知道否则将保持引用状态,则只需显式将其设置为 null。 例如,保留的数组。 当不再需要数组中的各个元素时,您可能希望将其清空。

例如,ArrayList 中的以下代码:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

此外,只要不保留任何引用,显式清空对象不会导致对象比自然超出范围的对象更快地被收集。

:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

和:

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

在功能上是等效的。

Typically, no.

But like all things: it depends. The GC in Java these days is VERY good and everything should be cleaned up very shortly after it is no longer reachable. This is just after leaving a method for local variables, and when a class instance is no longer referenced for fields.

You only need to explicitly null if you know it would remain referenced otherwise. For example an array which is kept around. You may want to null the individual elements of the array when they are no longer needed.

For example, this code from ArrayList:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

Also, explicitly nulling an object will not cause an object to be collected any sooner than if it just went out of scope naturally as long as no references remain.

Both:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

and:

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

Are functionally equivalent.

分開簡單 2024-07-19 02:51:10

根据我的经验,人们常常出于偏执而不是出于必要而取消引用。 以下是一个快速指南:

  1. 如果对象 A 引用对象 B 并且您不再需要此引用并且对象 A 不符合垃圾回收条件,那么您应该显式 null出场。 如果封闭对象无论如何都会被垃圾回收,则无需清空字段。 在 dispose() 方法中清空字段几乎总是无用的。

  2. 无需将方法中创建的对象引用置空。 一旦方法终止,它们将自动清除。 此规则的例外情况是,如果您正在运行一个很长的方法或某个大型循环,并且您需要确保在该方法结束之前清除一些引用。 同样,这些情况极为罕见。

我想说的是,绝大多数时候你不需要取消引用。 试图智取垃圾收集器是没有用的。 你最终只会得到低效、不可读的代码。

In my experience, more often than not, people null out references out of paranoia not out of necessity. Here is a quick guideline:

  1. If object A references object B and you no longer need this reference and object A is not eligible for garbage collection then you should explicitly null out the field. There is no need to null out a field if the enclosing object is getting garbage collected anyway. Nulling out fields in a dispose() method is almost always useless.

  2. There is no need to null out object references created in a method. They will get cleared automatically once the method terminates. The exception to this rule is if you're running in a very long method or some massive loop and you need to ensure that some references get cleared before the end of the method. Again, these cases are extremely rare.

I would say that the vast majority of the time you will not need to null out references. Trying to outsmart the garbage collector is useless. You will just end up with inefficient, unreadable code.

×眷恋的温暖 2024-07-19 02:51:10

好文章是今天的编码恐怖

GC 的工作方式是查找没有任何指针的对象,其搜索区域是堆/堆栈以及它们拥有的任何其他空间。 因此,如果将变量设置为 null,则实际对象现在不会被任何人指向,因此可能会被 GC 回收。

但由于 GC 可能不会在那一刻运行,因此您实际上可能不会给自己购买任何东西。 但是,如果您的方法相当长(就执行时间而言),那么可能是值得的,因为您将增加 GC 收集该对象的机会。

代码优化也可能使问题变得复杂,如果在将变量设置为 null 后不再使用该变量,则删除将值设置为 null 的行(少执行一条指令)将是一种安全的优化。 所以你实际上可能没有得到任何改进。

总而言之,是的,它可以提供帮助,但它不是确定性的

Good article is today's coding horror.

The way GC's work is by looking for objects that do not have any pointers to them, the area of their search is heap/stack and any other spaces they have. So if you set a variable to null, the actual object is now not pointed by anyone, and hence could be GC'd.

But since the GC might not run at that exact instant, you might not actually be buying yourself anything. But if your method is fairly long (in terms of execution time) it might be worth it since you will be increasing your chances of GC collecting that object.

The problem can also be complicated with code optimizations, if you never use the variable after you set it to null, it would be a safe optimization to remove the line that sets the value to null (one less instruction to execute). So you might not actually be getting any improvement.

So in summary, yes it can help, but it will not be deterministic.

沫离伤花 2024-07-19 02:51:10

至少在java中,它根本不是巫术编程。 当您使用类似的方法在 java 中创建对象时,您

Foo bar = new Foo();

会执行两件事:首先,创建对对象的引用,其次,创建 Foo 对象本身。 只要该引用或另一个引用存在,就无法对特定对象进行垃圾回收。 但是,当您将 null 分配给该引用时...

bar = null ;

并假设没有其他任何东西具有对该对象的引用,则该对象将被释放并可在垃圾收集器下次经过时用于 gc。

At least in java, it's not voodoo programming at all. When you create an object in java using something like

Foo bar = new Foo();

you do two things: first, you create a reference to an object, and second, you create the Foo object itself. So long as that reference or another exists, the specific object can't be gc'd. however, when you assign null to that reference...

bar = null ;

and assuming nothing else has a reference to the object, it's freed and available for gc the next time the garbage collector passes by.

邮友 2024-07-19 02:51:10

这取决于。

一般来说,您保留对对象的引用的时间越短,它们被收集的速度就越快。

如果您的方法需要 2 秒才能执行,并且在方法执行一秒后您不再需要对象,则清除对它的任何引用是有意义的。 如果 GC 在一秒钟后发现您的对象仍然被引用,下次它可能会在一分钟左右检查它。

无论如何,默认情况下将所有引用设置为 null 对我来说是过早的优化,没有人应该这样做,除非在特定的罕见情况下它可以显着减少内存消耗。

It depends.

Generally speaking shorter you keep references to your objects, faster they'll get collected.

If your method takes say 2 seconds to execute and you don't need an object anymore after one second of method execution, it makes sense to clear any references to it. If GC sees that after one second, your object is still referenced, next time it might check it in a minute or so.

Anyway, setting all references to null by default is to me premature optimization and nobody should do it unless in specific rare cases where it measurably decreases memory consuption.

显式设置对 null 的引用而不只是让变量超出范围,对垃圾收集器没有帮助,除非保存的对象非常大,在完成处理后立即将其设置为 null 是一个好主意。

一般将引用设置为null,对于代码的读者来说意味着该对象已经完全完成并且不应该再关心了。

通过放入一组额外的大括号来引入更窄的范围,可以实现类似的效果,

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

这允许在离开嵌套大括号后立即对 bigThing 进行垃圾收集。

Explicitly setting a reference to null instead of just letting the variable go out of scope, does not help the garbage collector, unless the object held is very large, where setting it to null as soon as you are done with is a good idea.

Generally setting references to null, mean to the READER of the code that this object is completely done with and should not be concerned about any more.

A similar effect can be achieved by introducing a narrower scope by putting in an extra set of braces

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

this allows the bigThing to be garbage collected right after leaving the nested braces.

梦里梦着梦中梦 2024-07-19 02:51:10
public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

上面的程序抛出OutOfMemoryError。 如果取消注释 data = null;OutOfMemoryError 就会得到解决。 将未使用的变量设置为 null 始终是一个好习惯

public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

Above program throws OutOfMemoryError. If you uncomment data = null;, the OutOfMemoryError is solved. It is always good practice to set the unused variable to null

脱离于你 2024-07-19 02:51:10

有一次,我正在开发一个视频会议应用程序,当我不再需要该对象时,我花时间将引用设为空引用,结果发现性能存在巨大差异。 那是在 2003-2004 年,我只能想象自那以后 GC 变得更加智能。 就我而言,每秒都有数百个对象进出范围,因此当 GC 定期启动时,我注意到了它。 然而,在我将其指向空对象后,GC 停止暂停我的应用程序。

所以这取决于你做什么...

I was working on a video conferencing application one time and noticed a huge huge huge difference in performance when I took the time to null references as soon as I didn't need the object anymore. This was in 2003-2004 and I can only imagine the GC has gotten even smarter since. In my case I had hundreds of objects coming and going out of scope every second, so I noticed the GC when it kicked in periodically. However after I made it a point to null objects the GC stopped pausing my application.

So it depends on what your doing...

つ低調成傷 2024-07-19 02:51:10

我假设OP指的是这样的事情:

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

在这种情况下,VM不会在它们离开作用域时立即将它们标记为GC吗?

或者,从另一个角度来看,显式地将这些项设置为 null 是否会导致它们在超出范围时先被 GC 处理? 如果是这样,当无论如何都不需要内存时,VM 可能会花费时间来 GC 对象,这实际上会导致 CPU 使用性能更差,因为它会更早地进行 GC 。

I assume the OP is referring to things like this:

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

In this case, wouldn't the VM mark them for GC as soon as they leave scope anyway?

Or, from another perspective, would explicitly setting the items to null cause them to get GC'd before they would if they just went out of scope? If so, the VM may spend time GC'ing the object when the memory isn't needed anyway, which would actually cause worse performance CPU usage wise because it would be GC'ing more earlier.

世界和平 2024-07-19 02:51:10

即使取消引用稍微更有效,是否值得在代码中添加这些丑陋的取消? 它们只会变得混乱并模糊包含它们的意图代码。

它是一个罕见的代码库,没有比试图智胜垃圾收集器更好的优化候选者(更罕见的是成功智胜垃圾收集器的开发人员)。 您的努力很可能会更好地花在其他地方,放弃那个粗俗的 Xml 解析器或寻找一些缓存计算的机会。 这些优化将更容易量化,并且不需要您用噪音弄脏您的代码库。

Even if nullifying the reference were marginally more efficient, would it be worth the ugliness of having to pepper your code with these ugly nullifications? They would only be clutter and obscure the intent code that contains them.

Its a rare codebase that has no better candidate for optimisation than trying to outsmart the Garbage collector (rarer still are developers who succeed in outsmarting it). Your efforts will most likely be better spent elsewhere instead, ditching that crufty Xml parser or finding some opportunity to cache computation. These optimisations will be easier to quantify and don't require you dirty up your codebase with noise.

恬淡成诗 2024-07-19 02:51:10

是的。

来自“务实程序员”第 292 页:

通过设置对 NULL 的引用,可以将指向该对象的指针数量减少一......(这将允许垃圾收集器将其删除)

Yes.

From "The Pragmatic Programmer" p.292:

By setting a reference to NULL you reduce the number of pointers to the object by one ... (which will allow the garbage collector to remove it)

眉目亦如画i 2024-07-19 02:51:10

这取决于

我不了解 Java,但在 .net(C#、VB.net...)中,当您不再需要对象时,通常不需要分配 null。

但请注意,“通常不需要”。

通过分析您的代码,.net 编译器可以很好地评估变量的生命周期...以准确判断对象何时不再被使用。 因此,如果你写 obj=null ,它实际上看起来好像 obj 仍在使用......在这种情况下,分配 null 会适得其反。

在某些情况下,分配 null 实际上可能会有所帮助。 一个例子是,您有一段运行很长时间的庞大代码,或者一个在不同线程或某个循环中运行的方法。 在这种情况下,分配 null 可能会有所帮助,以便 GC 很容易知道它不再被使用。

没有硬性的& 对此的快速规则。 按照上面的方法,在代码中进行空分配,并运行分析器以查看它是否有任何帮助。 您很可能看不到任何好处。

如果您要优化的是 .net 代码,那么我的经验是,认真对待 Dispose 和 Finalize 方法实际上比担心 null 更有益。

有关该主题的一些参考:

http://blogs.msdn。 com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx

"It depends"

I do not know about Java but in .net (C#, VB.net...) it is usually not required to assign a null when you no longer require a object.

However note that it is "usually not required".

By analyzing your code the .net compiler makes a good valuation of the life time of the variable...to accurately tell when the object is not being used anymore. So if you write obj=null it might actually look as if the obj is still being used...in this case it is counter productive to assign a null.

There are a few cases where it might actually help to assign a null. One example is you have a huge code that runs for long time or a method that is running in a different thread, or some loop. In such cases it might help to assign null so that it is easy for the GC to know its not being used anymore.

There is no hard & fast rule for this. Going by the above place null-assigns in your code and do run a profiler to see if it helps in any way. Most probably you might not see a benefit.

If it is .net code you are trying to optimize, then my experience has been that taking good care with Dispose and Finalize methods is actually more beneficial than bothering about nulls.

Some references on the topic:

http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx

以为你会在 2024-07-19 02:51:10

在将来执行程序时,某些数据成员的值将用于计算程序外部可见的输出。 其他可能会也可能不会被使用,取决于未来(并且无法预测)程序的输入。 其他数据成员可能保证不被使用。 分配给那些未使用的数据的所有资源(包括内存)都被浪费了。 垃圾收集器 (GC) 的工作就是消除浪费的内存。 对于 GC 来说,消除一些需要的东西将是灾难性的,因此所使用的算法可能是保守的,保留的数量超过了严格的最小值。 它可能会使用启发式优化来提高速度,但代价是保留一些实际不需要的项目。 GC 可能使用许多潜在的算法。 因此,您对程序所做的更改可能不会影响程序的正确性,但可能会影响 GC 的操作,要么使其运行得更快以完成相同的工作,要么更快地识别未使用的项目。 因此,这种将 unusdd 对象引用设置为 null 的更改,理论上并不总是巫术。

是巫毒吗? 据报道,Java 库代码的一部分可以执行此操作。 该代码的编写者比普通程序员要好得多,并且要么了解了解垃圾收集器实现细节的程序员,要么与了解垃圾收集器实现细节的程序员合作。 这表明有时是有好处的。

In the future execution of your program, the values of some data members will be used to computer an output visible external to the program. Others might or might not be used, depending on future (And impossible to predict) inputs to the program. Other data members might be guaranteed not to be used. All resources, including memory, allocated to those unused data are wasted. The job of the garbage collector (GC) is to eliminate that wasted memory. It would be disastrous for the GC to eliminate something that was needed, so the algorithm used might be conservative, retaining more than the strict minimum. It might use heuristic optimizations to improve its speed, at the cost of retaining some items that are not actually needed. There are many potential algorithms the GC might use. Therefore it is possible that changes you make to your program, and which do not affect the correctness of your program, might nevertheless affect the operation of the GC, either making it run faster to do the same job, or to sooner identify unused items. So this kind of change, setting an unusdd object reference to null, in theory is not always voodoo.

Is it voodoo? There are reportedly parts of the Java library code that do this. The writers of that code are much better than average programmers and either know, or cooperate with, programmers who know details of the garbage collector implementations. So that suggests there is sometimes a benefit.

两人的回忆 2024-07-19 02:51:10

正如你所说,有一些优化,即 JVM 知道变量最后一次使用的位置,并且它引用的对象可以在最后一点之后立即被 GC(仍在当前范围内执行)。 所以在大多数情况下清空引用并不能帮助GC。

但这对于避免“裙带关系”(或“浮动垃圾”)问题很有用(在此处了解更多信息观看视频)。 之所以存在这个问题,是因为堆被分为旧代和年轻代,并且应用了不同的 GC 机制:次要 GC(速度很快,经常发生在清理年轻代)和主要 GC(这会导致清理旧代的停顿时间更长)。 如果年轻一代的垃圾被已经属于老一代的垃圾引用,“裙带关系”不允许收集年轻一代的垃圾。

这是“病态的”,因为任何提升的节点都会导致所有后续节点的提升,直到 GC 解决问题为止。

为了避免裙带关系,最好将应该删除的对象的引用置空。 您可以在 JDK 类中看到此技术的应用:LinkedListLinkedHashMap

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}

As you said there are optimizations, i.e. JVM knows the place when the variable was last used and the object referenced by it can be GCed right after this last point (still executing in current scope). So nulling out references in most cases does not help GC.

But it can be useful to avoid "nepotism" (or "floating garbage") problem (read more here or watch video). The problem exists because heap is split into Old and Young generations and there are different GC mechanisms applied: Minor GC (which is fast and happens often to clean young gen) and Major Gc (which causes longer pause to clean Old gen). "Nepotism" does not allow for garbage in Young gen to be collected if it is referenced by garbage which was already tenured to an Old gen.

This is 'pathological' because ANY promoted node will result in the promotion of ALL following nodes until a GC resolves the issue.

To avoid nepotism it's a good idea to null out references from an object which is supposed to be removed. You can see this technique applied in JDK classes: LinkedList and LinkedHashMap

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}
拥抱我好吗 2024-07-19 02:51:10

Oracle 文档指出“将 null 分配给不再需要的变量” https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Oracle doc point out "Assign null to Variables That Are No Longer Needed" https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

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