我想知道如果在一个方法内声明一个本地线程会发生什么?通常,由于它们都是在堆栈上分配的,因此所有的局部变量在函数返回时都会消失。但是,似乎本地线程会有不同的情况。是这样吗?
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,直到其运行方法完成。即使本地方法完成并且线程仍然存活,这也是正确的。
示例:
public void doSomeAsync(){
Thread th = new Thread(new Runnable(){
public void run(){
Thread.sleep(500);
}
});
th.start();
//do something else quickly
}
在//快速执行其他操作
之后,任何未逃逸出该方法的定义都会被标记为垃圾回收。线程th不会被标记为垃圾回收,并且正确地放置在堆上,具有自己的线程栈。
John的回答很好,但我想补充一些细节。这是一个代码示例,我将使用它来展示特定变量的用法。
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
目标。
run()
方法完成并且线程被JVM收回后,该线程才会被垃圾回收。在线程被GC之后,所有由线程使用的字段都可以被GC。Thread
完成并被GC后,它也可以被GC。startThread()
方法结束并且堆栈被重用时,它将被覆盖。final
,所以线程不能使用它。在startThread()
完成后,它可以被GC。final
,因此线程可以使用它,但实际上并没有使用。在startThread()
完成后,它可以被GC。final
,并且被线程使用。只有在startThread()
方法完成并且Runnable
和Thread
被GC后,它才能被GC。Runnable
内部的局部字段,作为Runnable
匿名类的一部分分配在堆上。在Runnable
完成并且Runnable
和Thread
被GC后,它可以被GC。Runnable
内部的局部字段,分配在堆上。在Runnable
完成并且Runnable
和Thread
被GC后,它可以被GC。run()
方法内部的局部字段,分配在新线程的堆栈上。当run()
方法完成并且堆栈被重用时,它将被覆盖。run()
方法内部的局部字段,分配在堆上。在run()
方法完成后,它可以被GC。baz()
方法内部的局部字段,分配在新线程的堆栈上。当baz()
方法完成并且堆栈被重用时,它将被覆盖。baz()
方法内部的局部字段,分配在堆上。在baz()
方法完成后,它可以被GC。另外需要注意的几点:
startThread()
方法结束后它可以被垃圾回收。与之相关的Runnable
和所有变量也会被回收。startThread()
方法中声明的final long varX
原始数据类型,那么它必须被分配到堆上而不是栈上。当startThread()
方法结束后,它仍然处于使用状态。startThread()
完成后将其GC。与之相关的所有变量也可以随之被GC。 - Grayrun
方法完成执行。Runnable
实例(或包含线程主体的任何内容)将具有该原始值的副本。Runnable
将具有该相同引用的副本(因此将保持该对象处于活动状态)。Thread
,那么只有被声明为final
的本地方法变量会一直存在,直到Thread
完成。当Thread
完成其run()
方法时,线程和任何来自创建它的方法的可用的最终变量都将像其他所有内容一样被垃圾回收。
澄清
只有在原始方法和生成的线程的run()
方法中使用的final
变量才会在两个方法都完成之前不被垃圾回收。如果线程没有访问该变量,则线程的存在不会阻止在原始方法完成后对变量进行垃圾回收。
参考资料
http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html
Runnable
的实例变量中。(在Java 8中,你甚至不需要将方法变量声明为final就可以工作-只要它们未更改即可,也就是说,如果你愿意,你可以将它们标记为final。) - yshavit