7

这是一个理论问题。我只是好奇,如果 Java 有一种垃圾收集机制来释放内存,那为什么还会OutOfMemoryError发生呢?

我搜索了这个,但可以获得这些链接

垃圾收集器是否保证在内存不足错误之前运行?

OutOfMemoryError 之前的垃圾收集

这些并没有具体回答我的问题。如果 Java 通过使用垃圾收集来很好地允许内存,那么为什么会OutOfMemoryError发生呢?

4

7 回答 7

12

如果内存中没有对象符合垃圾回收条件,则可能发生 OutOfMemoryError。例如:

List<MyClass> hellos = new ArrayList<>();
for (;;) {
   hellos.add(new MyClass());
}

这将创建一个列表并不断向其中添加MyClass对象,直到内存耗尽。没有一个对象符合垃圾回收的条件,因为它们都有对它们的引用。

于 2013-06-25T02:25:13.123 回答
3

也可以以您意想不到的方式隔离内存。考虑以下字符串示例。

char[] chars      = new char[10_000_000];  // May need to adjust.
String string     = new String(chars);
chars             = null;
String substring  = string.substring(5_000_000);
string            = null; 

chars可以收集阵列。内部数组string可能不会被收集,因为substring包含对内部数组的引用,后跟偏移量和范围。因此,仍然分配了 10 7 个字符,即使只使用和访问了 5 * 10 6个字符。

Java 1.7.0_06

似乎String.substring不再有这种行为。在Java Performance Tuning Guide 网站的一篇文章中,Mikhail Vorontsov 报告说,在 Java 1.7.0_06 及更高版本中,String.substring总是创建一个String独立于旧版本的新版本。该类String不再具有offset实例range变量。创建一个大字符串、获取子字符串并丢弃原始字符串不会留下旧字符串的char[]隔离。

// Java 1.7.0_06 and up
char[] chars      = new char[10_000_000];  // May need to adjust.
String string     = new String(chars);
chars             = null;
String substring  = string.substring(5_000_000);
// Copies a portion of string's array to a new array.
string            = null;
// string's array is no longer reachable, and may be garbage collected.
于 2013-06-25T02:43:10.177 回答
2

因为并非所有内存都是垃圾。

于 2013-06-25T02:55:18.833 回答
1

垃圾收集器在不会被使用的内存上工作。

但是,如果您分配内存,并且该内存在范围内,则意味着垃圾收集器无法真正清理该内存。

当分配的内存高于 VM 允许的内存时,就会出现该异常。

于 2013-06-25T02:23:47.563 回答
1

无法收集仍然从运行代码中引用它的对象。因此,如果有一种方法可以在理论上访问一个对象,它就会被卡在内存中。

就是这么说的,Java 并没有消除关心内存的需要。它只是自动化了很多内存管理的东西。您仍然必须尽自己的一份力量,以确保您不会在对象的使用寿命结束后将其扔掉......因为只要它们可以到达(但间接地),它们就会占用无法回收的内存.

于 2013-06-25T02:24:09.953 回答
0

最好在您的工作完成后将您的对象无效化,这样您就可以确保垃圾收集器可以使用这些对象,以避免出现内存不足或内存泄漏的问题。

于 2013-06-25T02:50:49.523 回答
0

如果内存不足并且没有适合删除的变量或对象,则会引发 except。

所以使用 GC(垃圾收集)的最佳方式是:

如果不再使用该变量,则将其显式分配为空值。

于 2013-06-25T02:53:45.523 回答