我想知道如果您在方法中声明一个本地线程会发生什么?通常,一旦函数返回,所有局部变量都会消失,因为它们都分配在堆栈上。但是,似乎本地线程将是一个不同的故事。是对的吗?
public int A() {
Thread t = new Thread() {
doSomething();
}
t.start();
return -1;
}
我想知道如果您在方法中声明一个本地线程会发生什么?通常,一旦函数返回,所有局部变量都会消失,因为它们都分配在堆栈上。但是,似乎本地线程将是一个不同的故事。是对的吗?
public int A() {
Thread t = new Thread() {
doSomething();
}
t.start();
return -1;
}
线程是它自己的 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,而是正确地放置在具有自己的线程堆栈的堆上。
约翰的回答很好,但我想我会添加更多细节。这是一个代码示例,我将使用它来显示特定的变量用法。
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 收割。在Thread
GC'd 之后,所有使用的字段Thread
都可以 GC'd。Thread
完成后进行 GC 并进行 GC。startThread()
并在堆栈上分配。startThread()
当方法完成并重用堆栈时,它将被覆盖。startThread()
并在堆上分配。它不能被线程使用,因为它不是final
. 它可以在startThread()
完成后进行 GC。startThread()
并在堆上分配。这是final
因为它可以被线程使用,但事实并非如此。它可以在startThread()
完成后进行 GC。startThread()
并在堆上分配。这是final
并且它被线程使用。它只能在startThread()
方法完成并且和Runnable
都被 GC 后才能被Thread
GC。Runnable
并作为Runnable
匿名类的一部分分配在堆上。它可以在Runnable
完成后被 GC 处理,并且Runnable
和Thread
被 GC 处理。Runnable
并在堆上分配。它可以在Runnable
完成后被 GC 处理,并且Runnable
和Thread
被 GC 处理。run()
方法内部的一个本地字段,分配在新线程的堆栈上。run()
当方法完成并重用堆栈时,它将被覆盖。run()
方法内部的一个本地字段,并在堆上分配。run()
方法完成后可以对它进行 GC 。baz()
方法内部的一个本地字段,分配在新线程的堆栈上。baz()
当方法完成并重用堆栈时,它将被覆盖。baz()
方法内部的一个本地字段,并在堆上分配。baz()
方法完成后可以对它进行 GC 。其他注意事项:
startThread()
完成后进行 GC。和所有与之相关的Runnable
变量也可以被 GC 处理。final long varX
声明并使用了一个原语startThread()
,那么它必须分配在堆上而不是堆栈上。完成startThread()
后它仍将被使用。如果一个线程是从本地上下文启动的,那么线程将继续执行,直到它的 Runnablerun
方法完成执行。
如果变量是原始变量,那么它将在堆栈上,并且在方法返回时将消失——但是您的线程Runnable
实例(或任何包含线程内容的对象)将具有该原始值的副本。
如果变量是引用类型,则该对象在堆上分配并一直存在,直到不再有对它的引用,此时它有资格进行垃圾回收。对该对象的引用在堆栈上,并且在方法返回时将消失,但与原语一样,线程Runnable
将拥有相同引用的副本(从而使该对象保持活动状态)。
如果你Thread
在一个方法中产生一个局部变量,只有声明为的局部方法变量final
会一直存在直到Thread
完成。当Thread
完成它的run()
方法时,线程和它从创建它的方法中可用的任何最终变量将像其他所有东西一样被垃圾收集。
澄清
只有final
在原始方法和衍生线程的run()
方法中使用的变量才会被垃圾收集,直到方法和run()
方法都完成。如果线程不访问变量,那么线程的存在不会阻止变量在原始方法完成后被垃圾回收。
参考
http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html