关于Java内存泄漏的基础知识

时间:2012-07-31 13:23:14

标签: java memory-leaks

我读到Java is passed by value。因此,我们假设我们拥有此代码,并说HashMap somehashMap的生命周期比foo更长。所以foo不允许被垃圾收集,即使它已经完成它的工作只是因为我们把foo放在Map中然后忘了从它中删除。现在按照我链接到的帖子中的答案逻辑我们实际上是将foo的引用副本传递给方法put()对吗?在这种情况下放foo进入HashMap不应该阻止垃圾收集。你能帮我理解这里发生了什么吗?我到底错过了什么?

 public void someMethod(){
     Foo foo  = new Foo();
     somehashMap.put(fooKey,foo); 
  }

9 个答案:

答案 0 :(得分:11)

垃圾收集不适用于引用,而是适用于驻留在堆中的实际对象。当您将foo放入Map时,您基本上可以帮助它“逃脱”当前范围,并将其置于与somehashMap相同的范围/生命周期内。

Java中的引用在后台透明地处理。在地图中放置foo引用时,引用的副本实际上会传递给put方法调用,但底层对象即new Foo()是原始和复制的引用都相同。我们来看下面的片段:

public void doIt() {
  Object f1 = new Object();
  Object f2 = f1;
  Object f3 = f2;
}

在上面的代码片段中,执行doIt后垃圾收集了多少个对象?它只是我们创建的单个new Object()。 Rest all只是用于指向同一对象的引用或别名。

答案 1 :(得分:7)

只要可以访问对象,就不能对其进行垃圾回收。在你的情况下,foo可以通过Foo foo = someHashMap.get(fooKey);到达,所以只要它在地图中并且地图本身可以到达,就不能进行垃圾收集。

答案 2 :(得分:6)

我认为您不清楚参考实际上是什么。

'foo'是对象的引用,并将其添加到hashmap有效地创建了对同一对象的第二个引用,这是部分正确的。

然而,这就是为什么当你的'foo'变量消失时,对象不会被垃圾收集。只有当没有引用时,对象才被垃圾收集 - 你从一个引用(foo)开始,然后创建第二个(在hashmap内),当你的函数结束时第一个引用消失,但你仍然有一个引用。

答案 3 :(得分:5)

试一试:

enter image description here

框是对象。未装箱的字母是变量。

一开始你只有你的hashmap,我假设你的函数被调用之前存在,并且之后继续存在。

然后new Foo()创建一个新对象,f=创建一个引用它的变量。

然后hashmap.put(k,f)在hashmap中创建一个条目,该条目指向与f相同的对象。

然后当你退出函数时,局部变量f不再存在,但其他一切仍然存在。

垃圾收集器不会删除Foo对象,因为hashmap仍然指向它。这就是你想要的 - 如果你以后调用hashmap.get(k),你会期望得到那个Foo对象。

如果从散列映射中删除条目 - hashmap.remove(k),则可以对Foo对象进行垃圾回收。

如果hashmap本身停止存在,则可以对Foo对象进行垃圾回收。

答案 4 :(得分:3)

大多数使用垃圾收集器的语言都支持某种weak references。例如,您可以在Java中找到弱哈希映射实现。如果你将一个foo对象放入弱哈希映射并且垃圾收集器将运行,那么如果没有其他指向foo的指针(除了周指针)之外,将收集foo对象并从弱哈希映射中删除。有时你可以通过这种方式逃避内存泄漏。

答案 5 :(得分:2)

只要foo在HashMap中,它就不能被垃圾收集,因为Java不知道你再也不会使用它了。只要所有引用都是生命周期结束,就可以对foo进行垃圾回收。

答案 6 :(得分:2)

  

我们实际上是将foo引用的副本传递给方法   put()对吧?

,Java正在将实习地图保留reference的参考副本传递给内存中的same objectheap),因此它不会在你的地图之前有资格获得GC。

答案 7 :(得分:1)

put方法的传递值是foo的引用值。由于foo是引用计数,只要somehashMap保留引用,foo就不会收集垃圾。

我确实在某些代码中发现了这样的内存泄漏,我继承了有人使用地图来缓存图片但是当再次需要图像时,而不是抓住缓存的图像,无论如何都创建了一个新的并推入带有新密钥的地图。

答案 8 :(得分:1)

Java确实通过了值。但是,您按值传递引用,而不是对象的实例