是否有可能在Java中强制垃圾收集,即使这很棘手?我知道System.gc();和Runtime.gc ();但他们只建议做GC。如何强制GC?
当前回答
I did some experimentation (see https://github.com/mikenakis/ForcingTheJvmToGarbageCollect) trying about a dozen different ways of performing a garbage collection, including ways described in this answer, and more, and I found that there is absolutely no frigging way to deterministically force the JVM to do a complete garbage collection. Even the best answers to this question are only partially successful in that the best they achieve is some garbage collection, but never a guaranteed full garbage collection.
我的实验表明,下面的代码片段产生了最好的(最不坏的)结果:
public static void ForceGarbageCollection()
{
long freeMemory = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
for( ; ; )
{
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
long newFreeMemory = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
if( newFreeMemory == freeMemory )
break;
freeMemory = newFreeMemory;
sleep( 10 );
}
}
其中sleep()函数如下:
private static void sleep( int milliseconds )
{
try
{
Thread.sleep( milliseconds );
}
catch( InterruptedException e )
{
throw new RuntimeException( e );
}
}
不幸的是,睡眠中的数字10是有魔力的;它假设您每秒执行中等数量的内存分配,这将导致中等数量的结束。如果你通过对象的速度更快,那么10可能是不够的,你可能需要等待更长的时间。您可以将其设置为100,但无论您将其设置为多少,总有可能它不够用。
话虽如此,在一个受控制的环境中,10个就足够了,这种方法被观察到可以持续地从内存中消除所有不可访问的对象,而本问答中提到的其他方法都不能做到这一点。我在github上链接的实验代码证明了这一点。
在我看来,Java虚拟机没有提供执行强制的、无条件的、确定的、绝对彻底的、让世界停止的垃圾收集的方法,这一事实使它崩溃了。
换句话说,JVM的创造者是如此傲慢,以至于认为他们比我们更了解我们是否想要这样做,或者我们是否应该这样做。别那么傲慢。如果某些东西像魔法一样起作用,那么必须提供一些绕过魔法的方法。
其他回答
如果您正在运行内存并得到一个OutOfMemoryException,您可以尝试通过java -Xms128m -Xmx512m开始编程来增加java可用的堆空间的数量,而不仅仅是java。这将使您的初始堆大小为128Mb,最大堆大小为512Mb,远远超过标准的32Mb/128Mb。
在带有G1 GC的OracleJDK 10上,对System.gc()的单个调用将导致GC清理旧集合。我不确定GC是否立即运行。但是,即使在循环中多次调用System.gc(), GC也不会清理Young Collection。为了让GC清理Young Collection,你必须在一个循环中分配(例如new byte[1024]),而不调用System.gc()。出于某种原因调用System.gc()会阻止GC清理Young Collection。
jlibs库有一个很好的用于垃圾收集的实用程序类。您可以使用WeakReference对象的一个漂亮的小技巧强制垃圾收集。
RuntimeUtil.gc()从jlibs:
/**
* This method guarantees that garbage collection is
* done unlike <code>{@link System#gc()}</code>
*/
public static void gc() {
Object obj = new Object();
WeakReference ref = new WeakReference<Object>(obj);
obj = null;
while(ref.get() != null) {
System.gc();
}
}
我们可以使用java运行时触发jmap -histo:live <pid>。这将强制在堆上进行完整的GC,以标记所有活动对象。
public static void triggerFullGC() throws IOException, InterruptedException {
String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
Process process = Runtime.getRuntime().exec(
String.format("jmap -histo:live %s", pid)
);
System.out.println("Process completed with exit code :" + process.waitFor());
}
是的,几乎可以强迫你必须以相同的顺序调用方法,同时这些方法是:
System.gc ();
System.runFinalization ();
即使只是一个对象来清理,同时使用这两个方法强制垃圾收集器使用不可达对象的finalize()方法释放分配的内存并执行finalize()方法的状态。
不过是一个可怕的实践使用垃圾收集器,因为使用它可能会引入一个加载的软件可能甚至比在内存坏,垃圾收集器有自己的线程是不可能控制+根据gc可能需要更多的时间和所使用的算法是考虑非常低效,您应该检查您的软件如果它坏在gc的帮助,因为它绝对是坏,一个好的解决方案不能依赖于gc。
注意:只是要记住,只有在finalize方法中没有对对象进行重赋时,这才会起作用,如果发生这种情况,对象将保持存活,它将有一个复活,这在技术上是可能的。
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap