在Java中是否可以强制进行垃圾回收?即使有些棘手,我知道System.gc();
和Runtime.gc();
但它们只是建议进行GC。那么如何强制GC呢?
在Java中是否可以强制进行垃圾回收?即使有些棘手,我知道System.gc();
和Runtime.gc();
但它们只是建议进行GC。那么如何强制GC呢?
JVM规范没有具体说明垃圾回收。因此,供应商可以自由地按照自己的方式实现GC。
因此,这种含糊不清会导致垃圾回收行为的不确定性。您应该检查JVM详细信息以了解垃圾回收方法/算法。此外,还有定制行为的选项。
System.GC
保证会“做些什么” :) - rogerdpackDisposable
接口但在使用完后未调用dispose()
方法的大型对象(例如图形类)?您是否在类级别上声明了仅在单个方法中需要的内容?public abstract class Pool<T> {
private int mApproximateSize;
private LinkedList<T> mPool = new LinkedList<>();
public Pool(int approximateSize) {
mApproximateSize = approximateSize;
}
public T attain() {
T item = mPool.poll();
if (item == null) {
item = newInstance();
}
return item;
}
public void release(T item) {
int approxSize = mPool.size(); // not guaranteed accurate
if (approxSize < mApproximateSize) {
recycle(item);
mPool.add(item);
} else if (approxSize > mApproximateSize) {
decommission(mPool.poll());
}
}
public abstract T newInstance();
public abstract void recycle(T item);
public void decommission(T item) { }
}
如果您能描述一下为什么需要垃圾回收,那会更好。如果您正在使用SWT,可以释放资源(如Image
和Font
)以释放内存。例如:
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
还有一些工具可以确定未处理的资源。
Runtime.getRuntime().gc()
或使用实用程序方法 System.gc()
。注意:这些方法不能确保垃圾回收,并且它们的范围应该限制在JVM而不是在应用程序中编程处理它。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()
好。 - Mike Nakis我进行了一些实验(参见https://github.com/mikenakis/ForcingTheJvmToGarbageCollect),尝试了大约十多种不同的垃圾收集方式,包括此答案中描述的方式以及其他方式,但我发现没有任何可靠的方式可以确定性地强制JVM进行完整的垃圾回收。即使是对于这个问题的最佳答案,它们所能实现的也只是部分成功,最好的结果也只是进行了一些垃圾回收,但从未能够保证进行完全的垃圾回收。
我的实验证明,以下代码片段产生了最佳(最少糟糕)的结果:
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 );
}
}
很遗憾,sleep(10)
中的那个数字10
是有魔力的;它假定你每秒进行适量的内存分配,并且还会产生适度的最终化。如果你更快地通过对象,则10
可能不足,你可能需要等待更长时间。你可以将其设置为100
来确保,但无论你设置成什么,总会有一个机会,它可能不够。
话虽如此,在一个受控环境中,10
就足够了。这种方法被观察到能够始终从内存中清除所有不可达的对象,而在本问答中提到的其他任何方法都不能做到。我在github上链接的实验代码证明了这一点。
在我看来,Java虚拟机没有提供一种强制性、无条件、确定性、彻底、全面停顿的垃圾回收方法,因此它是有缺陷的。
换句话说,JVM的创造者们非常自负,认为他们比我们更清楚我们是否想要这样做或者是否应该想要这样做。不要如此傲慢。如果某些东西看起来像魔法一样奏效,那么必须提供绕过魔法的方法。我想强制进行垃圾回收,因为当它发生时我的代码被冻结了很长时间。目的是通过定期触发gc来平滑负载。
然而,在我的环境中,列出的解决方案并没有强制执行任何操作。
这很容易实现,但需要调整。
Runtime rt = Runtime.getRuntime();
double usedMB = (rt.totalMemory() - rt.freeMemory()) / 1024 / 1024;
if (usedMB > 1000) // only when necessary
{
byte[][] for_nothing = new byte[10][];
for (int k = 0; k < 10 ; k ++)
for_nothing[k] = new byte[100_000_000];
}
System.gc();
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
OutOfMemoryException
错误,你可以尝试通过使用java -Xms128m -Xmx512m
来启动程序,而不是仅仅使用java
。这将为你提供一个初始堆大小为128Mb和最大堆大小为512Mb的空间,远远超过标准的32Mb/128Mb。java -Xms512M -Xmx1024M
。 - ThePyroEagle这是正确的,只是猜测。您已经得到了几位发布者给出的标准答案。让我们逐个来看:
正确的,没有实际的jvm - 这只是一个规范,一堆计算机科学描述所需的行为...我最近深入研究了从本地代码初始化Java对象。要获得您想要的内容,唯一的方法是执行所谓的积极空值操作。如果做错了,错误是如此严重,我们必须限制自己在问题的原始范围内:
这里的大多数发布者都会认为您正在使用接口工作,如果是这样,我们需要查看您是否被交付整个对象还是一次一个项目。
如果您不再需要一个对象,可以将null赋值给该对象,但如果操作不当,则会生成空指针异常。如果您使用NIO,我敢打赌您可以实现更好的工作。
每当您、我或其他任何人都说:“请帮我,这太可怕了。”时,几乎总是意味着您正在尝试处理的内容即将被彻底摧毁……请为我们编写一小段示例代码,并从中清除任何实际使用的代码并展示您的问题。
不要感到沮丧。通常情况下,这意味着您的数据库管理员正在使用从其他地方购买的软件包,而原始设计并未针对大型数据结构进行调整。
这是非常普遍的情况。