【问题标题】:Basics about Java memory leak关于 Java 内存泄漏的基础知识
【发布时间】:2012-07-29 06:42:41
【问题描述】:

我读到了 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); 
  }

【问题讨论】:

  • Foo 与somehashMap 具有相同的生命周期

标签: java memory-leaks


【解决方案1】:

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

Java 中的引用在后台透明地处理。当您将 foo 引用放入映射时,reference 的副本实际上被传递给 put 方法调用,但底层对象(即 new Foo())对于原始对象和复制的引用。我们看下面的sn-p:

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

在上面的sn-p中,doIt执行完成后有多少个对象被垃圾回收了?这只是我们创建的单个new Object()。其余都是简单的引用或别名,用于指向同一个对象。

【讨论】:

    【解决方案2】:

    只要对象是可访问的,它就不能被垃圾回收。在您的情况下, foo 可以通过 Foo foo = someHashMap.get(fooKey); 访问,因此只要它在地图中并且地图本身可以访问,就不能被垃圾收集。

    【讨论】:

      【解决方案3】:

      我认为您并不清楚引用实际上是什么。

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

      然而,这正是当你的 'foo' 变量消失时对象不会被垃圾回收的原因。仅当没有剩余引用时才对对象进行垃圾收集 - 您从一个引用 (foo) 开始,然后创建第二个引用(在 hashmap 内),当您的函数结束时第一个引用消失,但您仍然有一个引用。

      【讨论】:

        【解决方案4】:

        试试图片:

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

        一开始你只有你的哈希图,我假设它在你的函数被调用之前就存在,然后继续存在。

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

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

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

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

        如果您从哈希图中删除条目 - hashmap.remove(k),则 Foo 对象可以被垃圾收集。

        如果 hashmap 本身不再存在,则可以对 Foo 对象进行垃圾收集。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

            只要 foo 在 HashMap 中,它就不能被垃圾回收,因为 Java 不知道你永远不会再使用它。 foo 可以在 所有 引用结束后立即被垃圾回收。

            【讨论】:

              【解决方案7】:

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

              是的,Java 正在将实习映射持有 reference 的引用的副本传递给内存中的same object (heap),因此在您的映射之前它没有资格进行 GC .

              【讨论】:

                【解决方案8】:

                在这个put方法的情况下传值是对foo的引用的值。由于foo 是引用计数的,只要somehashMap 持有引用,foo 就不会被垃圾回收。

                我确实在一些我继承的代码中发现了这样的内存泄漏,其中有人使用地图缓存图像,但是当再次需要图像时,不是抓取缓存的图像,而是创建了一个新的图像并将其推入带有新键的地图。

                【讨论】:

                  【解决方案9】:

                  Java 确实是按值传递的。但是,您通过值传递 reference,而不是对象的 instance。

                  【讨论】:

                    猜你喜欢
                    • 2011-01-05
                    • 1970-01-01
                    • 2014-09-10
                    • 1970-01-01
                    • 2011-06-01
                    • 2014-06-19
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多