【问题标题】:How to free memory in Java?如何在 Java 中释放内存?
【发布时间】:2010-12-06 18:45:01
【问题描述】:

有没有办法在 Java 中释放内存,类似于 C 的 free() 函数?还是将对象设置为 null 并依赖 GC 是唯一的选择?

【问题讨论】:

  • 好吧...让我们把一件事弄清楚。仅仅因为您认为某事是不好的做法,而不是值得鼓励做的事,并不值得一票否决。这是一个明确而有效的问题,询问是否有一种方法可以在不依赖垃圾收集的情况下释放 Java 中的内存。虽然它可能不鼓励并且通常没有用或一个好主意,但您无法知道在不知道 Felix 知道什么的情况下可能不需要它的场景。 Felix 甚至可能不打算使用它。他可能只是想知道这是否可能。它绝不值得一票否决。
  • 为澄清起见,这是针对投反对票的人 - 不一定是以前的 cmets。

标签: java garbage-collection


【解决方案1】:

Java 使用托管内存,因此分配内存的唯一方法是使用 new 运算符,而释放内存的唯一方法是依靠垃圾收集器。

memory management whitepaper (PDF) 可能有助于解释发生了什么。

您也可以致电System.gc() 建议垃圾收集器立即运行。但是,Java 运行时做出最终决定,而不是您的代码。

根据Java documentation,

调用 gc 方法表明 Java 虚拟机花费精力 朝着回收未使用的对象 为了让他们的记忆 目前占用可用于快​​速 重用。当控制权从 方法调用,Java 虚拟机 已尽最大努力收回 所有丢弃的对象的空间。

【讨论】:

  • 它确实强制垃圾收集器运行。它不会强制它释放内存...
  • 没有 Pablo,它不会强制 GC 运行。
  • 一位非常可靠的人告诉我,所有 HotSpotVM 的垃圾收集器都会完全忽略 System.gc()。
  • 在 winXp java SE GC 上运行每个 System.gc() 或几乎每个但 API doc 不保证。
  • @Pablo Santa Cruz 你是什么意思它不释放内存?我刚刚在似乎有泄漏的程序上对其进行了测试,并且 ram 的使用似乎稳定了?丹尼尔只是说它只是暗示了每次我调用该方法时,使用的内存百分比总是稳定的。你们让我很困惑。
【解决方案2】:

似乎没有人提到明确将对象引用设置为null,这是一种您可能需要考虑的“释放”内存的合法技术。

例如,假设您在一个方法的开头声明了一个List<String>,该方法的大小变得非常大,但直到方法执行到一半才需要。此时您可以将 List 引用设置为 null 以允许垃圾收集器在方法完成之前回收该对象(并且无论如何引用都超出了范围)。

请注意,我在现实中很少使用这种技术,但在处理非常大的数据结构时值得考虑。

【讨论】:

  • 如果你真的在一个只用于我建议的方法的一部分的对象上做很多工作;你的方法太复杂,把方法分成前后部分,或者在前半部分代码使用块(后半部分对测试脚本更有用)
  • 将对象引用设置为 null 很重要的地方是当它被另一个长期存在的对象(或可能来自静态变量)引用时。例如,如果您有一个长期存在的大对象数组,并且您停止使用其中一个对象,则应将数组引用设置为 null 以使该对象可用于 GC。
【解决方案3】:
System.gc(); 

运行垃圾收集器。

调用 gc 方法建议 Java 虚拟机花费精力回收未使用的对象,以使它们当前占用的内存可用于快速重用。当控制从方法调用返回时,Java 虚拟机已尽最大努力从所有丢弃的对象中回收空间。

不推荐。

编辑:我在 2009 年写了原始回复。现在是 2015 年。

在 Java 出现约 20 年的时间里,垃圾收集器已经变得越来越好。此时,如果您手动调用垃圾收集器,您可能需要考虑其他方法:

  • 如果您在有限数量的机器上强制执行 GC,则可能值得在当前机器远离的地方设置一个负载平衡器,等待它完成为连接的客户端提供服务,超时后一段时间挂起连接,然后硬重启JVM。这是一个糟糕的解决方案,但如果您正在查看 System.gc(),则强制重启可能是权宜之计。
  • 考虑使用不同的垃圾收集器。例如,(过去六年新推出的)G1 收集器是一款低暂停型号;它总体上使用了更多的 CPU,但最好不要强制执行硬停止。由于服务器 CPU 现在几乎都具有多个内核,因此这是一个非常好的折衷方案。
  • 查看调整内存使用的标志。尤其是在较新版本的 Java 中,如果您没有那么多长期运行的对象,请考虑增加堆中 newgen 的大小。 newgen (young) 是分配新对象的地方。对于网络服务器,为请求创建的所有内容都放在这里,如果这个空间太小,Java 将花费额外的时间将对象升级到寿命更长的内存,在那里它们被杀死的成本更高。 (如果 newgen 稍微太小,你就要付出代价。)例如,在 G1 中:
    • XX:G1NewSizePercent(默认为 5;可能无关紧要。)
    • XX:G1MaxNewSizePercent(默认为 60;可能会提高。)
  • 考虑告诉垃圾收集器,您不能再暂停一下。这将导致更频繁的 GC 运行,以允许系统保留其余的约束。在 G1 中:
    • XX:MaxGCPauseMillis(默认为 200。)

【讨论】:

  • 在我自己的帖子上评论,这通常没有任何作用,并且反复调用它会导致JVM变得不稳定等等。它也可能碾过你的狗;谨慎接近。
  • 我会重点强调“调用 gc 方法表明 JVM 扩展工作量”的“建议”部分
  • @Jesper,Dean 的回答是“建议”。事实上,他发布了该方法的 javadocs 中的确切文档...
  • @Software Monkey:是的,我可以直接编辑它。但是由于 Dean J 显然很活跃(仅在几分钟前发布),我认为请他这样做是一种礼貌。如果他没有,我会回到这里进行编辑并删除我的评论。
  • 我们也值得一说为什么不推荐它。如果 JVM 重视运行 GC 的“建议”,它几乎肯定会让你的应用运行速度变慢,可能会慢很多数量级!
【解决方案4】:

*“我个人依靠空变量作为占位符以供将来正确删除。例如,我在实际删除(使空)数组本身之前花时间将数组的所有元素清空。”

这是不必要的。 Java GC 的工作方式是找到没有引用它们的对象,所以如果我有一个带有引用(=变量)的对象 x 指向它,GC 不会删除它,因为有一个引用到那个对象:

a -> x

如果你将 a 设为空,就会发生这种情况:

a -> null
     x

所以现在 x 没有指向它的引用,将被删除。当您将 a 设置为引用与 x 不同的对象时,也会发生同样的事情。

因此,如果您有一个引用对象 x、y 和 z 的数组 arr 和一个引用该数组的变量 a,它看起来像这样:

a -> arr -> x
         -> y
         -> z

如果你将 a 设为空,就会发生这种情况:

a -> null
     arr -> x
         -> y
         -> z

因此 GC 发现 arr 没有对其设置引用并将其删除,这为您提供了以下结构:

a -> null
     x
     y
     z

现在 GC 找到 x、y 和 z 并删除它们。清空数组中的每个引用不会有任何好处,它只会占用代码中的 CPU 时间和空间(也就是说,它不会受到更多伤害。GC 仍然能够以它应该的方式执行)。

【讨论】:

    【解决方案5】:

    要扩展 Yiannis Xanthopoulos 和 Hot Licks 的回答和评论(抱歉,我还不能评论!),您可以像这个示例一样设置 VM 选项:

    -XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30
    

    在我的 jdk 7 中,如果在虚拟机空闲时 GC 后超过 30% 的堆变得空闲,这将释放未使用的虚拟机内存。您可能需要调整这些参数。

    虽然我没有在下面的链接中看到它强调,但请注意,一些垃圾收集器可能不遵守这些参数,默认情况下 java 可能会为你选择其中一个,如果你碰巧有多个核心(因此使用上面的 G1GC 参数)。

    VM arguments

    更新:对于 java 1.8.0_73,我看到 JVM 偶尔会使用默认设置释放少量。似乎只有在大约 70% 的堆未使用时才会这样做。不知道如果操作系统的物理内存不足,是否会更积极地释放。

    【讨论】:

      【解决方案6】:

      想要从任何程序(Java 或非 Java)中释放内存的一个正当理由是在操作系统级别为其他程序提供更多内存。如果我的 java 应用程序使用 250MB,我可能希望将其强制降低到 1MB,并使 249MB 可用于其他应用程序。

      【讨论】:

      • 如果您需要在 Java 程序中显式释放 249MB 的块,内存管理不会是我想要处理的第一件事。
      • 但释放 Java 堆内的存储空间并不会(在一般情况下)使存储空间可供其他应用使用。
      【解决方案7】:

      我已经对此进行了实验。

      System.gc(); 确实只是建议运行垃圾收集器。

      但是在设置所有对null的引用后调用System.gc();,会提高性能和内存占用。

      【讨论】:

      • 我认为你不能肯定地说“调用 System.gc(); 将所有引用设置为 null 后,会提高性能和内存占用。”。因为 System.gc() 的计算复杂度很大。即使在调用 System.gc() 并且确实收集了垃圾之后,jvm 也可能不会将内存还给操作系统或系统。 JVM 可能会保留内存以供将来参考。看到这个answer。
      【解决方案8】:

      如果你真的想分配和释放一块内存,你可以使用直接的 ByteBuffers 来做到这一点。甚至还有一种非便携的方式来释放内存。

      但是,正如建议的那样,仅仅因为您必须在 C 中释放内存,并不意味着必须这样做。

      如果你觉得你真的有一个很好的 free() 用例,请将它包含在问题中,以便我们看到你正在做什么,很可能有更好的方法。

      【讨论】:

        【解决方案9】:

        完全来自javacoffeebreak.com/faq/faq0012.html

        低优先级线程自动处理垃圾收集 为用户。在空闲时间,线程可能会被调用,并且它 可以开始释放以前分配给 Java 对象的内存。 但请放心 - 它不会删除您身上的物品!

        当没有对对象的引用时,它就变成了公平的游戏 垃圾收集器。而不是调用一些例程(比如 free in C++),您只需将对象的所有引用分配给 null,或者 为引用分配一个新类。

        示例:

        public static void main(String args[])
        {
          // Instantiate a large memory using class
          MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);
        
          // Do some work
          for ( .............. )
          {
              // Do some processing on myClass
          }
        
          // Clear reference to myClass
          myClass = null;
        
          // Continue processing, safe in the knowledge
          // that the garbage collector will reclaim myClass
        }
        

        如果您的代码即将请求大量内存,您可以 想要请求垃圾收集器开始回收空间,而不是 而不是允许它作为低优先级线程这样做。为此,添加 以下是您的代码

        System.gc();
        

        垃圾收集器将尝试回收可用空间,并且您的 应用程序可以继续执行,并回收尽可能多的内存 可能(内存碎片问题可能适用于某些平台)。

        【讨论】:

          【解决方案10】:

          在我的情况下,由于我的 Java 代码打算在不久的将来移植到其他语言(主要是 C++),我至少想口头上说要正确释放内存,这样它有助于以后的移植过程。

          我个人依靠空变量作为占位符,以便将来正确删除。例如,在实际删除(使为 null)数组本身之前,我花时间使数组的所有元素无效。

          但我的情况非常特殊,我知道这样做会影响性能。

          【讨论】:

            【解决方案11】:

            * "例如,假设您在一个列表的开头声明了一个列表 方法变得非常大,但只需要 直到方法进行到一半。你可以在这一点上设置 列出对 null 的引用以允许垃圾收集器潜在地 在方法完成之前回收这个对象(以及引用 无论如何都超出了范围)。” *

            这是正确的,但此解决方案可能无法推广。虽然将 List 对象引用设置为 null - 将使内存可用于垃圾收集,但这仅适用于原始类型的 List 对象。如果 List 对象包含引用类型,则设置 List object = null 将不会取消引用列表中包含的任何引用类型。在这种情况下,设置 List object = null 将孤立包含的引用类型,其对象将不可用于垃圾收集,除非垃圾收集算法足够聪明,可以确定对象已被孤立。

            【讨论】:

            • 这其实不是真的。 Java 垃圾收集器足够聪明,可以正确处理。如果您为 List 清空(并且 List 中的对象没有其他对它们的引用),G​​C 可以回收 List 中的所有对象。它目前可能选择不这样做,但最终会收回它们。循环引用也是如此。基本上,GC 的工作方式是专门寻找孤立对象,然后回收它们。这是 GC 的全部工作。您描述它的方式会使 GC 完全无用。
            【解决方案12】:

            虽然 java 提供了自动垃圾回收,但有时您会想知道对象有多大以及剩余多少。使用import java.lang; 和Runtime r=Runtime.getRuntime(); 以编程方式释放内存,以使用mem1=r.freeMemory(); 来释放内存值内存调用r.gc();方法和调用freeMemory()

            【讨论】:

              【解决方案13】:

              来自 JAVA 的建议是分配给 null

              来自https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

              为不再需要的变量显式分配空值有助于垃圾收集器识别可以安全回收的内存部分。尽管 Java 提供了内存管理,但它并不能防止内存泄漏或使用过多的内存。

              应用程序可能会因不释放对象引用而导致内存泄漏。这样做会阻止 Java 垃圾收集器回收这些对象,并导致使用的内存量增加。在使用后显式取消对变量的引用允许垃圾收集器回收内存。

              检测内存泄漏的一种方法是使用分析工具并在每次事务后拍摄内存快照。处于稳定状态的无泄漏应用程序将在垃圾回收后显示稳定的活动堆内存。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-12-08
                • 2012-01-18
                • 2011-11-03
                • 2023-04-05
                • 1970-01-01
                • 2011-01-31
                相关资源
                最近更新 更多