Java 中的循环引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/176745/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 10:54:28  来源:igfitidea点击:

Circular References in Java

javamemorymemory-managementmemory-leaksgarbage-collection

提问by Ryan Delucchi

Given an aggregation of class instances which refer to each other in a complex, circular, fashion: is it possible that the garbage collector may not be able to free these objects?

给定以复杂的循环方式相互引用的类实例的聚合:垃圾收集器是否可能无法释放这些对象?

I vaguely recall this being an issue in the JVM in the past, but I thoughtthis was resolved years ago. yet, some investigation in jhat has revealed a circular reference being the reason for a memory leak that I am now faced with.

我依稀记得这是过去 JVM 中的一个问题,但我认为这在几年前就已经解决了。然而,在 jhat 中的一些调查显示循环引用是我现在面临的内存泄漏的原因。

Note: I have always been under the impression that the JVM was capable of resolving circular references and freeing such "islands of garbage" from memory. However, I am posing this question just to see if anyone has found any exceptions.

注意:我一直认为 JVM 能够解析循环引用并从内存中释放此类“垃圾岛”。但是,我提出这个问题只是为了看看是否有人发现了任何异常。

采纳答案by David G

Only a very naive implementation would have a problem with circular references. Wikipedia has a good articleon the different GC algorithms. If you really want to learn more, try (Amazon) Garbage Collection: Algorithms for Automatic Dynamic Memory Management . Java has had a good garbage collector since 1.2 and an exceptionally good one in 1.5 and Java 6.

只有非常幼稚的实现才会有循环引用的问题。维基百科有一篇关于不同 GC 算法的好文章。如果您真的想了解更多信息,请尝试 (Amazon) Garbage Collection: Algorithms for Automatic Dynamic Memory Management。Java 从 1.2 开始就有一个很好的垃圾收集器,在 1.5 和 Java 6 中有一个非常好的垃圾收集器。

The hard part for improving GC is reducing pauses and overhead, not basic things like circular reference.

改进 GC 的难点在于减少暂停和开销,而不是像循环引用这样的基本内容。

回答by Rob Walker

The garbage collector knows where the root objects are: statics, locals on the stack, etc and if the objects aren't reachable from a root then they will be reclaimed. If they are reachable, then they need to stick around.

垃圾收集器知道根对象在哪里:静态、堆栈上的局部变量等,如果对象不能从根访问,那么它们将被回收。如果他们可以到达,那么他们需要留下来。

回答by anjanb

The garbage collector is a very sophisticated piece of software -- it has been tested in a huge JCK test-suite. It is NOT perfect BUT there is a very good chance that as long as the java compiler(javac) will compile all of your classes and JVM will instantiate it, then you should be good.

垃圾收集器是一个非常复杂的软件——它已经在一个巨大的 JCK 测试套件中进行了测试。它并不完美,但很有可能只要 java 编译器(javac)会编译你所有的类并且 JVM 会实例化它,那么你应该很好。

Then again, if you are holding references to the root of this object graph, the memory will NOT be freed BUT if you know what you're doing, you should be OK.

再说一次,如果你持有对这个对象图的根的引用,内存不会被释放,但是如果你知道你在做什么,你应该没问题。

回答by Thilo

No, at least using Sun's official JVM, the garbage collector will be able to detect these cycles and free the memory as soon as there are no longer any references from the outside.

不,至少使用 Sun 的官方 JVM,垃圾收集器将能够检测到这些循环并在不再有任何来自外部的引用时释放内存。

回答by Paul Tomblin

Just to amplify what has already been said:

只是为了放大已经说过的内容:

The application I've been working on for six years recently changed from Java 1.4 to Java 1.6, and we've discovered that we've had to add static references to things that we didn't even realize were garbage collectable before. We didn't need the static reference before because the garbage collector used to suck, and it is just so much better now.

我已经工作了六年的应用程序最近从 Java 1.4 更改为 Java 1.6,我们发现我们必须添加静态引用,这些引用我们之前甚至没有意识到是可垃圾回收的。我们之前不需要静态引用,因为垃圾收集器过去很糟糕,现在好多了。

回答by Will Hartung

Reference counting GCs are notorious for this issue. Notably, Suns JVM doesn't use a reference counting GC.

引用计数 GC 因这个问题而臭名昭著。值得注意的是,Suns JVM 不使用引用计数 GC。

If the object can not be reach from the root of the heap (typically, at a minimum, through the classloaders if nothing else0, then the objects will be destroyed as they are not copied during a typical Java GC to the new heap.

如果对象不能从堆的根访问(通常,至少,如果没有别的,通过类加载器0,那么对象将被销毁,因为它们在典型的 Java GC 期间没有被复制到新堆。

回答by Tim Williscroft

The Java specification says that the garbage collector can garbage collect your object ONLY If it is not reachable from any thread.

Java 规范说垃圾收集器只能在无法从任何线程访问您的对象时对其进行垃圾收集

Reachable means there is a reference, or chain of references that leads from A to B, and can go via C,D,...Z for all it cares.

可达意味着有一个引用或引用链从 A 到 B,并且可以通过 C、D、...Z 进行所有它关心的事情。

The JVM not collecting things has not been a problem for me since 2000, but your mileage may vary.

自 2000 年以来,JVM 不收集东西对我来说不是问题,但您的里程可能会有所不同。

Tip: Java serialization caches objects to make object mesh transfer efficient. If you have many large, transient objects, and all your memory is getting hogged, reset your serializer to clear it's cache.

提示:Java 序列化缓存对象以提高对象网格传输效率。如果您有许多大的、瞬态的对象,并且您的所有内存都被占用了,请重置您的序列化程序以清除它的缓存。

回答by Uri

If I remember correctly, then according to the specifications, there are only guarantees about what the JVM can't collect(anything reachable), not what it will collect.

如果我没记错的话,那么根据规范,只能保证 JVM 不能收集什么(任何可到达的东西),而不是它会收集什么。

Unless you are working with real-time JVMs, most modern garbage collectors should be able to handle complex reference structures and identify "subgraphs" that can be eliminated safely. The efficiency, latency, and likelihood of doing this improve over time as more research ideas make their way into standard (rather than research) VMs.

除非您使用实时 JVM,否则大多数现代垃圾收集器应该能够处理复杂的引用结构并识别可以安全消除的“子图”。随着越来越多的研究想法进入标准(而不是研究)虚拟机,效率、延迟和这样做的可能性会随着时间的推移而提高。

回答by Alexander

Ryan, judging by your comment to Circular References in Java, you fell into the trap of referencing objects from a class, which was probably loaded by the bootstrap/system classloader. Every class is referenced by the classloader that loaded the class, and can thus be garbage-collected only if the classloader is no longer reachable. The catch is that the bootstrap/system classloader is never garbage collected, therefore, objects reachable from classes loaded by the system classloader cannot be garbage-collected either.

Ryan,根据您对Java 循环引用的评论判断,您陷入了从类引用对象的陷阱,该类可能由引导程序/系统类加载器加载。每个类都被加载该类的类加载器引用,因此只有当类加载器不再可访问时才可以进行垃圾回收。问题是引导程序/系统类加载器永远不会被垃圾收集,因此,从系统类加载器加载的类可访问的对象也不能被垃圾收集。

The reasoning for this behavior is explained in JLS. For example, Third Edition 12.7 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7.

JLS 中解释了这种行为的原因。例如,第三版 12.7 http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.7

回答by Rupesh

A circular reference happens when one object refers to another, and that other one refers to the first object. For example:

当一个对象引用另一个对象,而另一个对象引用第一个对象时,就会发生循环引用。例如:

class A {
private B b;

public void setB(B b) {
    this.b = b;
}
}

class B {
private A a;

public void setA(A a) {
    this.a = a;
}
}

public class Main {
public static void main(String[] args) {
    A one = new A();
    B two = new B();

    // Make the objects refer to each other (creates a circular reference)
    one.setB(two);
    two.setA(one);

    // Throw away the references from the main method; the two objects are
    // still referring to each other
    one = null;
    two = null;
}
}

Java's garbage collector is smart enough to clean up the objects if there are circular references, but there are no live threads that have any references to the objects anymore. So having a circular reference like this does not create a memory leak.

如果存在循环引用,Java 的垃圾收集器足够聪明,可以清理对象,但不再有对对象有任何引用的活动线程。因此,拥有这样的循环引用不会造成内存泄漏。