3

我想知道如果您在方法中声明一个本地线程会发生什么?通常,一旦函数返回,所有局部变量都会消失,因为它们都分配在堆栈上。但是,似乎本地线程将是一个不同的故事。是对的吗?

public int A() {
    Thread t = new Thread() {
        doSomething();
    }
    t.start();
    return -1;
 }
4

5 回答 5

14

线程是它自己的 GC 根。因此,无论何时创建一个线程,不管它的创建上下文如何,它都不会准备好进行 GC,直到它的 run 方法完成。即使本地方法完成并且线程仍然存在,也是如此。

例子:

public void doSomeAsync(){
   Thread th = new Thread(new Runnable(){
      public void run(){
          Thread.sleep(500);
      }
   });
   th.start();
   //do something else quickly
}

//do somethign else quickly任何未转义该方法的定义之后,然后将其标记为 GC。线程 th 不会被标记为 GC,而是正确地放置在具有自己的线程堆栈的堆上。

于 2012-05-11T15:51:08.417 回答
7

约翰的回答很好,但我想我会添加更多细节。这是一个代码示例,我将使用它来显示特定的变量用法。

 public void startThread() {
      long var1 = 10;
      byte[] var2 = new byte[1024];
      final byte[] var3 = new byte[1024];
      final byte[] var4 = new byte[1024];
      Thread thread = new Thread(new Runnable() {
          private long var5 = 10;
          private byte[] var6 = new byte[1024];
          public void run() {
              int var7 = 100;
              byte[] var8 = new byte[1024];
              System.out.println("Size of var4 is " + var4.length);
              baz();
              ...
          }
          private void baz() {
              long var9 = 2;
              byte[] var10 = new byte[1024];
              ...
          }
      });
      thread.start();
 }

所以我们在这里围绕一个线程分配了许多变量。我们还拥有Thread对象本身以及Runnable线程正在运行的目标。

  • 线程——虽然它看起来是本地的startThread(),但关联Thread的也由 JVM 管理。它只是在run()方法完成后才被 GC 处理,并且Thread被 JVM 收割。在ThreadGC'd 之后,所有使用的字段Thread都可以 GC'd。
  • Runnable——这个匿名类是线程正在运行的。它可以在Thread完成后进行 GC 并进行 GC。
  • var1 - 这是本地的startThread()并在堆栈上分配。startThread()当方法完成并重用堆栈时,它将被覆盖。
  • var2 - 这是本地的startThread()并在堆上分配。它不能被线程使用,因为它不是final. 它可以在startThread()完成后进行 GC。
  • var3 - 这是本地的startThread()并在堆上分配。这是final因为它可以被线程使用,但事实并非如此。它可以在startThread()完成后进行 GC。
  • var4 - 这是本地的startThread()并在堆上分配。这是final并且它被线程使用。它只能在startThread()方法完成并且Runnable都被 GC 后才能被ThreadGC。
  • var5 - 这是内部的一个本地字段,Runnable并作为Runnable匿名类的一部分分配在堆上。它可以在Runnable完成后被 GC 处理,并且RunnableThread被 GC 处理。
  • var6 - 这是在堆内的一个本地字段Runnable并在堆上分配。它可以在Runnable完成后被 GC 处理,并且RunnableThread被 GC 处理。
  • var7 -- 这是run()方法内部的一个本地字段,分配在新线程的堆栈上。run()当方法完成并重用堆栈时,它将被覆盖。
  • var8 -- 这是run()方法内部的一个本地字段,并在堆上分配。run()方法完成后可以对它进行 GC 。
  • var9 -- 这是baz()方法内部的一个本地字段,分配在新线程的堆栈上。baz()当方法完成并重用堆栈时,它将被覆盖。
  • var10 -- 这是baz()方法内部的一个本地字段,并在堆上分配。baz()方法完成后可以对它进行 GC 。

其他注意事项:

  • 如果新线程从未启动,那么它可以在startThread()完成后进行 GC。和所有与之相关的Runnable变量也可以被 GC 处理。
  • 如果您在线程中final long varX声明并使用了一个原语startThread(),那么它必须分配在堆上而不是堆栈上。完成startThread()后它仍将被使用。
于 2012-05-11T16:24:56.140 回答
0

如果一个线程是从本地上下文启动的,那么线程将继续执行,直到它的 Runnablerun方法完成执行。

于 2012-05-11T15:46:19.133 回答
0

如果变量是原始变量,那么它将在堆栈上,并且在方法返回时将消失——但是您的线程Runnable实例(或任何包含线程内容的对象)将具有该原始值的副本。

如果变量是引用类型,则该对象在堆上分配并一直存在,直到不再有对它的引用,此时它有资格进行垃圾回收。对该对象的引用在堆栈上,并且在方法返回时将消失,但与原语一样,线程Runnable将拥有相同引用的副本(从而使该对象保持活动状态)。

于 2012-05-11T15:49:08.370 回答
-1

如果你Thread在一个方法中产生一个局部变量,只有声明为的局部方法变量final会一直存在直到Thread完成。当Thread完成它的run()方法时,线程和它从创建它的方法中可用的任何最终变量将像其他所有东西一样被垃圾收集。

澄清

只有final在原始方法和衍生线程的run()方法中使用的变量才会被垃圾收集,直到方法和run()方法都完成。如果线程不访问变量,那么线程的存在不会阻止变量在原始方法完成后被垃圾回收。

参考

http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html

于 2012-05-11T15:47:48.123 回答