简而言之:我有一个完成运行的线程,但没有收集垃圾。
长:请参阅以下示例代码:
public void saveSomething() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// heavy memory access (~400 MB), finishes after ~10sec
}
});
thread.start();
}
好的,所以这个帖子会在一段时间后开始并完成。在此期间使用了大量内存,这没关系。但我的问题是:
线程完成后,内存未设置为空闲
而且我不知道如何确保这一点。 完成的线程,不再使用,应该收到垃圾收集,据我所知;但这似乎不会发生在这里:(
请参阅jvisualVM的截图:
1:我通过调用saveSomething()
2:线程很久以前就完成了(我通过调试看到了它),然后我按下了#34;执行GC"在jvisualvm
正如您所看到的,在我强制GC之后,一切都按照我想要的方式运行。但这必须自动发生。我怎么能这样做,我做错了什么?
如果您需要更多信息,请询问。 注意:似乎在一天(希望更短)后,内存恢复正常,可能GC只是“慢”"或者不是经常定时?
答案 0 :(得分:34)
你问的是错误的问题吗?我的意思是:垃圾收集不会“立即”出现问题吗?
我很确定 - 当你启动另一个需要大量内存的线程时,GC会自行启动。
如果“延迟”对您来说实际上是一个问题,您可能会考虑进行“非常深入的研究”,以了解GC实际上如何为您的JVM版本工作;然后开始使用现有的许多命令行选项来根据您的需要微调GC行为。
但是,如果释放内存(对其他Java对象)的“延迟”不是问题;然后不要开始修复它。
答案 1 :(得分:22)
线程完成后不会发生垃圾收集。垃圾收集会在发生时发生。当您通过visualvm强制进行垃圾收集时,正确收集线程及其资源意味着一切正常。如果你等待足够长的时间或做更多消耗堆的事情,那么最终你的线程将被GC。
修改强> 唯一需要注意的是,即使没有引用,正在运行的线程也不会被垃圾回收。
答案 2 :(得分:18)
我同意以前的答案。该程序演示了“问题”并非特定于线程创建的事实。通常,gc在需要时或在特别要求时完成。
public class Test {
public static long memInUse(){
Runtime r = Runtime.getRuntime();
return r.totalMemory()-r.freeMemory();
}
public static void main(String[] args){
System.out.println("Initial memory: "+memInUse());
long[] bigArray = new long[1000000];
System.out.println("Memory after array allocation: "+memInUse());
long endTime = System.currentTimeMillis()+10000;
while(System.currentTimeMillis() < endTime){
System.out.println("While array exists: "+memInUse());
try{
Thread.sleep(1000);
}catch(InterruptedException e){
// Deliberately ignore the exception.
}
}
bigArray = null;
System.out.println("After null assignment: "+memInUse());
endTime = System.currentTimeMillis()+10000;
while(System.currentTimeMillis() < endTime){
System.out.println("While array reference null: "+memInUse());
try{
Thread.sleep(1000);
}catch(InterruptedException e){
// Deliberately ignore the exception.
}
}
System.gc();
System.out.println("After gc: "+memInUse());
}
}
输出:
Initial memory: 2684536
Memory after array allocation: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
After null assignment: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
After gc: 1622584
答案 3 :(得分:8)
完全有可能JVM在线程完成后根本不需要执行GC,因为它有足够的空闲堆。
只有在需要释放内存时才会执行GC。
就个人而言,我不担心。只要你知道当GC发生时内存将被清除,那就没关系了。
如果你真的想强制它,你必须在线程完成之后做一个显式的System.GC,或者作为run()方法的最后一行(假设没有进一步的引用)那个阶段的大数据。)
答案 4 :(得分:3)
可用内存浪费内存。如果你有2 GB的可用内存,那么你可以减少2 GB。释放记忆本身并不是有益的,它不会自动加速。
垃圾收集不是免费的。实际上它可能会占用大量CPU资源。发生的次数越少越好(从CPU负载角度来看)。
考虑到这一点,您通常不希望垃圾收集器在线程完成时立即启动。如果您此刻不需要更多RAM,那么就没有必要在GC上刻录CPU周期。大多数情况下,您可以信任JVM在需要时运行GC。
额外奖励:程序未使用的RAM可用于操作系统的缓存。在Windows上,这部分RAM显示为免费,在Linux中显示为使用,但实际上它介于两者之间。缓存不是必需的,但它可以提高性能。操作系统将自动管理它:当有更多可用RAM时,可以为缓存分配更多内容,当您内存不足时,缓存大小会减少。因此,有时拥有更多可用内存可能对性能有好处,但大多数时候你不必担心它。
答案 5 :(得分:1)
通常,在以下情况下,对象有资格在Java中进行垃圾收集:
1)该对象的所有引用都明确设置为null,例如object = null
2)在块内创建对象,并且一旦控制退出阻塞,引用就会超出范围。
3)父对象设置为null,如果对象保存另一个对象的引用,并且当您设置容器对象的引用null时,子对象或包含对象自动符合垃圾回收的条件。
4)如果一个对象只有通过WeakHashMap的实时弱引用,它将有资格进行垃圾收集。
有一些方法,如System.gc()和Runtime.gc(),用于向垃圾收集发送垃圾收集请求但不保证垃圾收集会发生。
回答你的问题。你需要强制垃圾收集。
System.gc ();
System.runFinalization ();
其他例子
public class GCTest {
public static void main(String[] args) throws InterruptedException {
A a = new A("white");
a = null;
Runtime.getRuntime().gc();
}
}
class A {
private String color;
public A(String color) {
this.color = color;
}
@Override
public void finalize() {
System.out.println(this.color + " cleaned");
}
}
警告使用垃圾收集器是一种可怕的做法,因为使用垃圾收集器可能会给软件带来过载甚至可能比内存更糟,垃圾收集器有自己的垃圾收集器根据gc使用的算法,无法控制的线程可能需要更多的时间而且考虑效率非常低,你应该在gc的帮助下检查你的软件是否最糟糕,因为它肯定是坏的,一个好的解决方案必须不依赖于gc。