我读到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);
}
答案 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)
试一试:
框是对象。未装箱的字母是变量。
一开始你只有你的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 object
(heap
),因此它不会在你的地图之前有资格获得GC。
答案 7 :(得分:1)
此put
方法的传递值是foo
的引用值。由于foo
是引用计数,只要somehashMap
保留引用,foo
就不会收集垃圾。
我确实在某些代码中发现了这样的内存泄漏,我继承了有人使用地图来缓存图片但是当再次需要图像时,而不是抓住缓存的图像,无论如何都创建了一个新的并推入带有新密钥的地图。
答案 8 :(得分:1)
Java确实通过了值。但是,您按值传递引用,而不是对象的实例。