这是一个理论问题。我只是好奇,如果Java有一个垃圾收集机制来释放内存,那么为什么还会出现OutOfMemoryError
?
我搜索了SO,但可以获得这些链接
Is the garbage collector guaranteed to run before Out of Memory Error?
Garbage collection before OutOfMemoryError
这些不具体回答我的问题。如果Java通过使用垃圾收集很好地允许内存,那么OutOfMemoryError
为什么会出现?
答案 0 :(得分:12)
如果内存中的所有对象都不符合垃圾回收的条件,则可能发生OutOfMemoryError。例如:
List<MyClass> hellos = new ArrayList<>();
for (;;) {
hellos.add(new MyClass());
}
这将创建一个列表并继续向其添加MyClass
个对象,直到内存耗尽。没有任何对象符合垃圾收集的条件,因为它们都引用了所有对象。
答案 1 :(得分: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 并且可以访问。
String.substring
似乎不再有这种行为。在Java Performance Tuning Guide web site的一篇文章中,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.
隔离。
{{1}}
答案 2 :(得分:2)
因为并非所有记忆都是垃圾。
答案 3 :(得分:1)
垃圾收集器可以处理不会被使用的内存。
但是,如果你分配内存,并且该内存在范围内,这意味着,垃圾收集器无法真正清理该内存。
当分配的内存太高于VM允许的内存时,就会出现异常。
答案 4 :(得分:1)
无法收集仍在运行代码中引用它的对象。因此,如果有一种理论上甚至可以访问对象的方法,它就会卡在内存中。
正如所说的那样,Java并没有消除关心内存的需要;它只是自动化了很多内存管理的东西。你仍然必须尽自己的一份力量,以确保你的生命周期结束后你不会躲开物体...因为只要它们可以到达(无论间接地),它们就会占用无法回收的记忆
答案 5 :(得分:0)
最好在使用它们完成工作后使对象无效,这样就可以确保垃圾收集器可以使用这些对象,以避免内存不足或内存泄漏等问题。
答案 6 :(得分:0)
如果内存不足且没有变量或对象可以删除,则会引发除外。
使用GC(垃圾收集)的最佳方法是:
如果不再使用该变量,则将该变量显式指定为空值。