有没有办法在 Java 中释放内存,类似于 C 的 free()
函数?还是将对象设置为 null 并依赖 GC 是唯一的选择?
Java 使用托管内存,因此分配内存的唯一方法是使用 new
运算符,而释放内存的唯一方法是依靠垃圾收集器。
此memory management whitepaper (PDF) 可能有助于解释发生了什么。
您也可以调用 System.gc()
来建议垃圾收集器立即运行。但是,Java 运行时做出最终决定,而不是您的代码。
调用 gc 方法表明 Java 虚拟机花费精力回收未使用的对象,以使它们当前占用的内存可用于快速重用。当控制从方法调用返回时,Java 虚拟机已尽最大努力从所有丢弃的对象中回收空间。
似乎没有人提到明确地将对象引用设置为 null
,这是一种您可能需要考虑的“释放”内存的合法技术。
例如,假设您在一个方法的开头声明了一个 List<String>
,该方法的大小变得非常大,但直到方法执行到一半才需要。此时您可以将 List 引用设置为 null
以允许垃圾收集器在方法完成之前回收此对象(并且无论如何引用都超出了范围)。
请注意,我在现实中很少使用这种技术,但在处理非常大的数据结构时值得考虑。
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。)
*“我个人依靠空变量作为将来正确删除的占位符。例如,在实际删除(使空)数组本身之前,我会花时间使数组的所有元素无效。”
这是不必要的。 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 仍然能够以它应该的方式执行)。
要扩展 Yiannis Xanthopoulos 和 Hot Licks 的回答和评论(对不起,我还不能评论!),您可以设置 VM 选项,如下例所示:
-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30
在我的 jdk 7 中,如果在虚拟机空闲时 GC 后超过 30% 的堆空闲,这将释放未使用的虚拟机内存。您可能需要调整这些参数。
虽然我没有在下面的链接中看到它强调,但请注意,一些垃圾收集器可能不遵守这些参数,默认情况下 java 可能会为您选择其中一个,如果您碰巧有多个内核(因此上面的 UseG1GC 参数)。
更新:对于 java 1.8.0_73,我看到 JVM 偶尔会使用默认设置释放少量。似乎只有在大约 70% 的堆未使用时才会这样做。不知道如果操作系统的物理内存不足,是否会更积极地释放。
想要从任何程序(Java 或非 Java)中释放内存的一个正当理由是在操作系统级别为其他程序提供更多内存。如果我的 java 应用程序使用 250MB,我可能希望将其强制降低到 1MB,并使 249MB 可用于其他应用程序。
我已经对此进行了实验。
确实,System.gc();
只建议运行垃圾收集器。
但是在设置所有对 null
的引用后调用 System.gc();
会提高性能和内存占用。
如果你真的想分配和释放一块内存,你可以使用直接的 ByteBuffers 来做到这一点。甚至还有一种非便携的方式来释放内存。
但是,正如已经建议的那样,仅仅因为您必须在 C 中释放内存,并不意味着必须这样做是一个好主意。
如果你觉得你真的有一个很好的 free() 用例,请将它包含在问题中,以便我们可以看到你正在做什么,很可能有更好的方法。
完全来自 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();垃圾收集器将尝试回收可用空间,您的应用程序可以继续执行,尽可能多地回收内存(内存碎片问题可能适用于某些平台)。
就我而言,由于我的 Java 代码打算在不久的将来移植到其他语言(主要是 C++),所以我至少想口头上说要正确释放内存,以便稍后帮助移植过程。
我个人依靠空变量作为将来正确删除的占位符。例如,在实际删除(使为空)数组本身之前,我花时间使数组的所有元素无效。
但我的情况非常特殊,我知道这样做会影响性能。
"例如,假设您在一个方法的开头声明了一个 List,该方法的大小变得非常大,但仅在方法执行到一半时才需要。此时您可以将 List 引用设置为 null允许垃圾收集器在方法完成之前回收这个对象(无论如何引用都超出了范围)。” *
这是正确的,但这种解决方案可能无法推广。虽然将 List 对象引用设置为 null - 将使内存可用于垃圾收集,但这仅适用于原始类型的 List 对象。如果 List 对象包含引用类型,则设置 List object = null 将不会取消引用列表中包含的任何引用类型。在这种情况下,设置 List object = null 将孤立包含的引用类型,其对象将不可用于垃圾收集,除非垃圾收集算法足够聪明,可以确定对象已被孤立。
通过 java 提供自动垃圾回收,有时您会想知道对象有多大以及剩余多少。使用编程方式释放内存 import java.lang;
和 Runtime r=Runtime.getRuntime();
以获取内存值,使用 mem1=r.freeMemory();
释放内存调用r.gc();
方法和调用 freeMemory()
来自 JAVA 的建议是分配给 null
来自https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html
将空值显式分配给不再需要的变量有助于垃圾收集器识别可以安全回收的内存部分。尽管 Java 提供了内存管理,但它并不能防止内存泄漏或使用过多的内存。应用程序可能会因不释放对象引用而导致内存泄漏。这样做会阻止 Java 垃圾收集器回收这些对象,并导致使用的内存量增加。在使用后显式取消对变量的引用允许垃圾收集器回收内存。检测内存泄漏的一种方法是使用分析工具并在每次事务后拍摄内存快照。处于稳定状态的无泄漏应用程序将在垃圾回收后显示稳定的活动堆内存。
System.gc()
。