Java“死”对象没有被垃圾收集

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5349924/
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-10-30 10:43:54  来源:igfitidea点击:

Java "dead" objects not being garbage collected

javamemory-leaksgarbage-collection

提问by Victor Blaga

I know that during garbage collection in Java, objects that don't have any more references to them are marked as "dead" so that they can be deleted from memory by the garbage collector.

我知道在 Java 中的垃圾收集过程中,没有更多引用的对象被标记为“死”,以便垃圾收集器可以将它们从内存中删除。

My question is if, during a garbage collection phase, all of the "dead" objects get deleted from memory or some of them survive? Why would a "dead" object survive a garbage collection phase?

我的问题是,在垃圾收集阶段,是否所有“死”对象都从内存中删除,或者其中一些仍然存在?为什么“死”对象会在垃圾收集阶段幸存下来?

LATER EDIT

稍后编辑

Thank you for all of your answers. I can deduce that the main reason why "dead" objects would not be deleted is due to timing or spacing limitations of the way the Garbage Collector operates. However, supposing that the Garbage Collector can reach all of the "dead" objects, I was wondering if there is a way to declare, reference, use, dereference, etc.. an object such that somehow it would skip the deletion phase even though it is "dead". I was thinking maybe objects belonging to classes which have static methods or inner classes or something like that may be kept in memory for some reason, even though they have no references to them.
Is such a scenario possible?

感谢您的所有回答。我可以推断出“死”对象不会被删除的主要原因是垃圾收集器操作方式的时间或间隔限制。但是,假设垃圾收集器可以访问所有“死”对象,我想知道是否有一种方法可以声明、引用、使用、取消引用等......一个对象,即使它会以某种方式跳过删除阶段它是“死的”。我在想也许属于具有静态方法或内部类的类的对象可能出于某种原因保留在内存中,即使它们没有对它们的引用。
这样的场景可能吗?

Thank you

谢谢

回答by Stephen C

My question is if, during a garbage collection phase, all of the "dead" objects get deleted from memory or some of them survive? Why would a "dead" object survive a garbage collection phase?

我的问题是,在垃圾收集阶段,是否所有“死”对象都从内存中删除,或者其中一些仍然存在?为什么“死”对象会在垃圾收集阶段幸存下来?

All current HotSpot GCs are generational collectors. Quoting from Wikipedia:

当前所有的 HotSpot GC 都是分代收集器。引自维基百科:

"It has been empirically observed that in many programs, the most recently created objects are also those most likely to become unreachable quickly (known as infant mortality or the generational hypothesis). A generational GC (also known as ephemeral GC) divides objects into generations and, on most cycles, will place only the objects of a subset of generations into the initial white (condemned) set. Furthermore, the runtime system maintains knowledge of when references cross generations by observing the creation and overwriting of references. When the garbage collector runs, it may be able to use this knowledge to prove that some objects in the initial white set are unreachable without having to traverse the entire reference tree. If the generational hypothesis holds, this results in much faster collection cycles while still reclaiming most unreachable objects."

“根据经验观察,在许多程序中,最近创建的对象也是那些最有可能很快变得无法访问的对象(称为婴儿死亡率或分代假设)。分代 GC(也称为临时 GC)将对象分成几代并且,在大多数循环中,只会将代的子集的对象放入初始的白色(谴责)集。此外,运行时系统通过观察引用的创建和覆盖来维护何时引用跨代的知识。当垃圾收集器运行,它或许可以利用这些知识来证明初始白集中的某些对象是不可达的,而不必遍历整个参考树。如果分代假设成立,这导致了更快的收集周期,同时仍然可以回收大多数无法访问的对象。”

What this means for your question is that most GC cycles collect only garbage objects in young generations. A garbage object in the oldest generation can survive multiple GC cycles ... until the old generation is finally collected. (And in the new G1 GC, apparently the old generation is collected a bit at a time ... which can delay reclamation even further.)

对于您的问题,这意味着大多数 GC 周期仅收集年轻代中的垃圾对象。最老年代的垃圾对象可以存活多个 GC 周期……直到老年代最终被回收。(而在新的 G1 GC 中,显然老年代会一次收集一点……这会进一步延迟回收。)

Other causes for (notionally) unreachable objects to survive include:

(名义上)无法访问的对象存活的其他原因包括:

  • Unreachable objects with (unexecuted) finalizers are attached to a finalization queue by the garbage collector for processing after the GC has finished.

  • Objects that are softly, weakly or phantom referenced are actually still reachable, and are handled by their respective reference queue managers after the GC has finished.

  • Objects that are reachable by virtue of JNI global references, etcetera. (thanks @bestss)

  • Various hidden references exist that relate instances, their classes and their classloaders.

  • There is a hidden reference from an inner instance to its outer instance.

  • There is a hidden reference from a class to the intern'd String objects that represent its string literals.

  • 具有(未执行的)终结器的无法访问的对象由垃圾收集器附加到终结队列,以便在 GC 完成后进行处理。

  • 软引用、弱引用或幻像引用的对象实际上仍然可以访问,并在 GC 完成后由它们各自的引用队列管理器处理。

  • 可通过 JNI 全局引用等访问的对象。(感谢@bestss)

  • 存在各种隐藏的引用,它们将实例、它们的类和它们的类加载器相关联。

  • 从内部实例到其外部实例有一个隐藏的引用。

  • 有一个从类到代表其字符串文字的实习字符串对象的隐藏引用。

However, these are all consequences of the definition of reachability:

然而,这些都是可达性定义的结果:

"A reachable object is any object that can be accessed in any potential continuing computation from any live thread."- JLS 12.6.1

“可达对象是可以在任何潜在的持续计算中从任何活动线程访问的任何对象。” - JLS 12.6.1

It is also worth noting that the rules for the GC have an element of conservativeness about them. They say that a reachable object won't be deleted, but they don't say that an object that is (strictly) unreachable will be deleted. This allows for cases where an object cannot be accessed but the runtime system is unable to figure that out.

还值得注意的是,GC 的规则对它们有一些保守的元素。他们说可达的对象不会被删除,但他们并没有说(严格地)不可达的对象会被删除。这允许无法访问对象但运行时系统无法弄清楚的情况。



Your followup question:

您的后续问题:

However, supposing that the Garbage Collector can reach all of the "dead" objects, I was wondering if there is a way to declare, reference, use, dereference, etc.. an object such that somehow it would skip the deletion phase even though it is "dead".

但是,假设垃圾收集器可以访问所有“死”对象,我想知道是否有一种方法可以声明、引用、使用、取消引用等......一个对象,即使它会以某种方式跳过删除阶段它是“死的”。

"Dead" is not a well-defined term. If the garbage collector can reach the objects, they are by definition reachable. They will not be deleted while they are still reachable.

“死”不是一个定义明确的术语。如果垃圾收集器可以访问这些对象,则它们根据定义是可达的。当它们仍然可以访问时,它们不会被删除。

If they are both dead AND reachable (whatever "dead" means!) then the fact that they are reachable means they won't be deleted.

如果它们都已死且可访问(无论“死”是什么意思!),那么它们可访问的事实意味着它们不会被删除。

What you are proposing doesn't make sense.

你的提议没有意义。

I was thinking maybe objects belonging to classes which have static methods or inner classes or something like that may be kept in memory for some reason, even though they have no references to them. Is such a scenario possible?

我在想也许属于具有静态方法或内部类的类的对象可能出于某种原因保留在内存中,即使它们没有对它们的引用。这样的场景可能吗?

Static methods don't have references ... unless they happen to be on the call stack. Then the local variables may contain references just like any other method call. Normal reachability rules apply.

静态方法没有引用……除非它们恰好在调用堆栈上。然后局部变量可能像任何其他方法调用一样包含引用。正常可达性规则适用。

Static fields are GC roots, for as long as the class itself exists. Normal reachability rules apply.

只要类本身存在,静态字段就是 GC 根。正常可达性规则适用。

Instances of inner classes are no different to instance of other classes from a GC perspective. There can be a reference to an outer class instance in an inner class instance, but that leads to normal reachability.

从 GC 的角度来看,内部类的实例与其他类的实例没有什么不同。在内部类实例中可以有对外部类实例的引用,但这会导致正常的可达性。

In summary, there are some unexpected "causes" for reachability, but they are all a logical consequence of the definition of reachability.

综上所述,可达性有一些意想不到的“原因”,但它们都是可达性定义的逻辑结果。

回答by mmccomb

One potential explanation for an unreachable object not being collected is time. As of Java 1.5 the amount of time the JVM spends garbage collecting can be limited using on of the following options...

无法收集无法访问的对象的一种可能解释是时间。从 Java 1.5 开始,可以使用以下选项限制 JVM 花费在垃圾收集上的时间...

  • -XX:MaxGCPauseMillis
  • -XX:GCTimeRatio=<nnn>
  • -XX:MaxGCPauseMillis
  • -XX:GCTimeRatio=<nnn>

Both options are explained in detail here

此处详细解释了这两个选项

回答by Léo Germond

As the System.gc() javadocsays

正如System.gc() javadoc所说

When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.

当控制从方法调用返回时,Java 虚拟机已尽最大努力从所有丢弃的对象中回收空间。

From which you can infer that a call to the garbage collector does not insure that all unused object will be reclaimed. As the garbage collection can completely differ between implementation, no definitive answer can be given. There is even java implementations without any garbage collection.

从中您可以推断出对垃圾收集器的调用并不能确保所有未使用的对象都将被回收。由于垃圾收集在不同实现之间可能完全不同,因此无法给出明确的答案。甚至还有没有任何垃圾收集的java 实现。

回答by Victor Sorokin

  • There are dead objects in "young" generation and there are dead objects in "old" generation. If GC being performed in "minor GC", only dead objects from young generation will be collected.
  • Additionally, you can use finalize()method to stop VM from collecting your object by throwing exception from finalize()(at least, this is how I understand Object.finalize()javadoc: Any exception thrown by the finalizemethod causes the finalization of this object to be halted, but is otherwise ignored).
  • “年轻”代有死对象,“老”代有死对象。如果在“minor GC”中执行 GC,则只会收集年轻代的死对象。
  • 此外,您可以使用finalize()方法通过抛出异常来阻止 VM 收集您的对象finalize()(至少,这是我对Object.finalize()javadoc 的理解:finalize方法抛出的任何异常都会导致该对象的最终确定被暂停,否则会被忽略)。

回答by qbert220

The behaviour of the garbage collector is not fully specified. If a particular implementation choose not to collect certain objects, it is allowed to do so. This could be done to avoid spending large periods of time in the garbage collector, which could have detrimental effects to the operation of the application.

垃圾收集器的行为没有完全指定。如果特定实现选择不收集某些对象,则允许这样做。这样做可以避免在垃圾收集器中花费大量时间,这可能会对应用程序的运行产生不利影响。

回答by Jon Hanna

Imagine you had a collection which contained millions of small objects, most of which were not referenced anywhere else. If the only references to that collection was cleared, would you want the GC to spend a long time cleaning out those millions of small objects, or would you want it to do so over the course of several calls? In most cases, the latter would be better for the application.

想象一下,您有一个包含数百万个小对象的集合,其中大部分都没有在其他任何地方引用。如果对该集合的唯一引用被清除,您是否希望 GC 花费很长时间清除那数百万个小对象,还是希望它在多次调用过程中这样做?在大多数情况下,后者更适合应用程序。