假设我有以下代码
package memoryleak;
public class MemoryLeak {
public static int size;
static {
size = (int) (Runtime.getRuntime().maxMemory()*0.6);
}
public static void main(String[] args) throws InterruptedException {
{
byte[] data1 = new byte[size];
}
byte[] data2 = new byte[size];
}
}
此代码生成OutOfMemoryError。您可以使用一个变量分配使这个代码工作(它重写第一个数组使用的堆栈帧,并使make数组可用于垃圾收集)。这个难题解释了here。
{
byte[] data1 = new byte[size];
}
int i = 0;
byte[] data2 = new byte[size];
问题是:为什么以下代码仍无效?
Object o = new Object();
synchronized (o) {
byte[] data1 = new byte[size];
}
int i = 0;
byte[] data2 = new byte[size];
以下作品:
Object o = new Object();
synchronized (o) {
byte[] data1 = new byte[size];
}
int i = 0;
synchronized (o) {
byte[] data2 = new byte[size];
}
答案 0 :(得分:6)
我敢打赌,synchronized
会向框架添加一个元素,导致data1
向上移动一个广告位而不会被i
破坏。 synchronized
需要解锁锁定的同一对象,即使本地/字段发生更改。
synchronized
代码看起来像这样:
Object $sync = o;
$sync.lock();
try {
byte[] data1 = new byte[size];
} finally {
$sync.unlock();
}
所以拿最后一段代码:
Object o = new Object(); // Slot 0.
synchronized (o) { // Slot 1.
byte[] data1 = new byte[size]; // Slot 2.
}
int i = 0; // Slot 1.
synchronized (o) { // Slot 2. (clobbers data1, was slot 1)
byte[] data2 = new byte[size]; // Slot 3.
}
答案 1 :(得分:0)
谜题很有意思,但对于那些不想考虑(或者更重要的是依赖于)垃圾收集更加神秘的方面的实用程序员来说,只要不再需要就设置data1 = null
问题?如果是这样,我宁愿这样做,然后奇怪的同步块和虚拟变量魔术。
当然,令人遗憾的是,一旦数组超出范围,内存就不会被释放,这是人们在this thread中所希望的。
这应该在JVM中修复。
答案 2 :(得分:0)
所有这些行为都取决于实现。垃圾收集器在其自己的异步线程中运行,该线程与程序的同步行为无关。你根本不知道data1引用的数组何时会被垃圾收集 - 你只能希望它在超出范围之后的“合理”时间内发生/所有对它的引用都消失了。
如果您担心程序中的内存不足,可以使用System.gc()显式尝试触发垃圾回收循环。但即使这样也无法保证在分配data2时有足够的内存可用。调用System.gc()只是对运行时的一个提示,你现在想要一个垃圾回收周期。
在Java中,内存分配和释放是不确定的。垃圾收集器将在运行时运行,您无法在程序级别运行它。您发布的代码片段之间没有相关差异,因为gc行为是非确定性的,并且触发它的精确时刻是实现和系统相关的。有时这对您的应用程序来说是个问题 - 例如,如果它是操作系统或在内存受限的嵌入式设备中运行 - 并且您需要使用C ++或其他语言进行编码,其中内存管理是确定性的。但是,对于我们大多数人来说,我们只相信垃圾收集器会以合理合理的方式运行,并且对于大多数用途来说已经足够好了 - 尽管如您所见,您可以创建导致问题的人为代码。
更新:令人尴尬。正如其他一些评论者提醒我的那样,在jvm抛出OutOfMemory错误之前,会显式触发垃圾收集周期。但是,行为仍然是不确定的:正如this link所解释的那样,jvm并不能保证在一个垃圾收集周期中检测到所有死对象。
答案 3 :(得分:-1)
您在实例化之前依靠GC来收集?
你能做不到吗Object o = new Object();
byte[] data1 = new byte[size];
GC.Collect()
byte[] data2 = new byte[size];