垃圾收集器不会立即收集完成的线程

时间:2015-05-26 08:37:06

标签: java multithreading memory-management garbage-collection

简而言之:我有一个完成运行的线程,但没有收集垃圾。

长:请参阅以下示例代码:

public void saveSomething() {
    Thread thread = new Thread(new Runnable() {

         @Override
         public void run() {
             // heavy memory access (~400 MB), finishes after ~10sec
         }
    });
    thread.start();
}

好的,所以这个帖子会在一段时间后开始并完成。在此期间使用了大量内存,这没关系。但我的问题是:

线程完成后,内存未设置为空闲

而且我不知道如何确保这一点。 完成的线程,不再使用,应该收到垃圾收集,据我所知;但这似乎不会发生在这里:(

请参阅jvisualVM的截图:

enter image description here

1:我通过调用saveSomething()

来触发线程启动

2:线程很久以前就完成了(我通过调试看到了它),然后我按下了#34;执行GC"在jvisualvm

正如您所看到的,在我强制GC之后,一切都按照我想要的方式运行。但这必须自动发生。我怎么能这样做,我做错了什么?

如果您需要更多信息,请询问。 注意:似乎在一天(希望更短)后,内存恢复正常,可能GC只是“慢”"或者不是经常定时?

6 个答案:

答案 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)

  1. 可用内存浪费内存。如果你有2 GB的可用内存,那么你可以减少2 GB。释放记忆本身并不是有益的,它不会自动加速。

  2. 垃圾收集不是免费的。实际上它可能会占用大量CPU资源。发生的次数越少越好(从CPU负载角度来看)。

  3. 考虑到这一点,您通常不希望垃圾收集器在线程完成时立即启动。如果您此刻不需要更多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。