ChatGPT解决这个技术问题 Extra ChatGPT

如何在 Java 中释放内存?

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

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

D
Daniel Pryden

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 的使用似乎稳定了?丹尼尔只是说它只是暗示了每次我调用该方法时,使用的内存百分比总是稳定的。你们让我很困惑。
A
Adamski

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

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

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


如果您真的在一个仅用于我建议的方法的一部分的对象上做很多工作;你的方法太复杂了,把方法分成前后部分,或者对前半部分代码使用一个块(后半部分对测试脚本更有用)
将对象引用设置为 null 很重要的地方是当它被另一个长期存在的对象(或可能来自静态变量)引用时。例如,如果您有一个长期存在的大对象数组,并且您停止使用其中一个对象,您应该将数组引用设置为 null 以使该对象可用于 GC。
D
Dean J
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;可能会提高这个.)

XX:G1NewSizePercent(默认为 5;可能无关紧要。)

XX:G1MaxNewSizePercent(默认为 60;可能会提高这个值。)

考虑告诉垃圾收集器你不适合长时间的停顿。这将导致更频繁的 GC 运行,以允许系统保留其余的约束。在 G1 中:XX:MaxGCPauseMillis(默认为 200。)

XX:MaxGCPauseMillis(默认为 200。)


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

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

这是不必要的。 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 仍然能够以它应该的方式执行)。


n
nsandersen

要扩展 Yiannis Xanthopoulos 和 Hot Licks 的回答和评论(对不起,我还不能评论!),您可以设置 VM 选项,如下例所示:

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

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

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

VM arguments

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


Y
Yios

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


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

我已经对此进行了实验。

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

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


我认为您不能肯定地说“调用 System.gc(); 将所有引用设置为 null 后,将提高性能和内存占用。”。因为 System.gc() 的计算复杂度很大。甚至在调用 System.gc() &确实在收集垃圾,jvm 可能不会将内存还给操作系统或系统。 JVM 可能会保留内存以供将来参考。请参阅此answer
D
Darron

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

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

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


S
Stefan Falk

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

低优先级线程自动为用户处理垃圾收集。在空闲时间,线程可能会被调用,它可以开始释放以前分配给 Java 对象的内存。但别担心 - 它不会删除你身上的物品!当没有对对象的引用时,垃圾收集器就成了公平的游戏。无需调用某个例程(如 C++ 中的 free),您只需将对该对象的所有引用分配为 null,或者为该引用分配一个新类。示例 : public static void main(String args[]) { // 使用类 MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192) 实例化一个大内存; // Do some work for ( ................. ) { // 对 myClass 做一些处理 } // 清除对 myClass 的引用 myClass = null; // 继续处理,知道垃圾收集器将回收 myClass 是安全的 } 如果您的代码即将请求大量内存,您可能希望请求垃圾收集器开始回收空间,而不是允许它这样做所以作为一个低优先级的线程。为此,请将以下内容添加到您的代码 System.gc();垃圾收集器将尝试回收可用空间,您的应用程序可以继续执行,尽可能多地回收内存(内存碎片问题可能适用于某些平台)。


O
Oskuro

就我而言,由于我的 Java 代码打算在不久的将来移植到其他语言(主要是 C++),所以我至少想口头上说要正确释放内存,以便稍后帮助移植过程。

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

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


不要将每个值都归零。这是对 CPU 时间和手动工作的浪费。让 GC 做它应该做的工作。一般来说,以错误的语言风格编写代码是一个非常糟糕的主意。如果您编写 Java,那么您应该编写 Java,而不是 C++。否则你会得到糟糕的、难以维护的代码,代码的主要部分实际上并没有做任何事情/根本没有意义。
G
Gothri

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

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


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

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


C
Community

来自 JAVA 的建议是分配给 null

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

将空值显式分配给不再需要的变量有助于垃圾收集器识别可以安全回收的内存部分。尽管 Java 提供了内存管理,但它并不能防止内存泄漏或使用过多的内存。应用程序可能会因不释放对象引用而导致内存泄漏。这样做会阻止 Java 垃圾收集器回收这些对象,并导致使用的内存量增加。在使用后显式取消对变量的引用允许垃圾收集器回收内存。检测内存泄漏的一种方法是使用分析工具并在每次事务后拍摄内存快照。处于稳定状态的无泄漏应用程序将在垃圾回收后显示稳定的活动堆内存。