我对GC在Java中的工作方式感到困惑。
以下是使我感到困惑的代码段:
private Data data = new Data();
void main() {
for (int i = 0; i < 100 ; i++) {
MyThread thread = new MyThread(data);
thread.start();
}
System.gc();
// Long running process
}
class MyThread extends Thread {
private Data dataReference;
MyThread(Data data) {
dataReference = data;
}
}
在上面的示例中,如果在继续操作之前调用了 gc (//长时间运行的进程)
是否将对本地线程进行垃圾回收?
否则,GC会将它们(MyThread本地引用)标记为活动状态,因为它持有对全局引用 data 的引用?
答案 0 :(得分:5)
MyThread
实例只有在完成它们的run
方法之后才可以被垃圾回收。在for循环结束之后,MyThread
方法已完成的run
的任何实例都可能被垃圾回收(因为没有对它们的引用)。
每个MyThread
实例都引用了一个未收集垃圾的Data
实例,这一事实并不影响MyThread
实例有资格进行垃圾收集的时间
答案 1 :(得分:2)
您的M: 16.672
T: 23.393
S: 2.609
实例在完成运行后才有资格进行垃圾回收。
根据定义,可以访问任何活动(即已启动但未终止)线程的线程堆栈和局部变量。
reachable 对象是可以从任何活动线程进行任何潜在的连续计算中访问的任何对象。 (JLS 12.6.1)
此外,由于活动线程可以调用MyThread
,因此只要线程处于活动状态,该线程的Thread.currentThread()
对象也必须是可访问的……而与该线程的任何其他引用无关。
但是,如果在调用Thread
方法之前 之前对Thread
对象的引用不可用,则 可以进行垃圾回收采集。如果不是这样,创建而不启动start()
将会导致内存泄漏!
答案 2 :(得分:1)
您始终可以调用垃圾回收,但是不能保证它可以同时运行。 (可能取决于系统,也可能取决于系统)。因为垃圾回收在守护进程线程(低优先级线程)下运行。
如果无法通过任何活动线程或任何静态引用访问该对象,则该对象可以进行垃圾回收或GC。换句话说,如果对象的所有引用均为null,则可以说它有资格进行垃圾回收。循环依赖项不算作引用,因此,如果对象A对对象B的引用,对象B对对象A的引用,并且它们没有任何其他实时引用,则对象A和对象B都可以进行垃圾回收。
答案 3 :(得分:1)
在System.gc();
调用之后没有gc会被执行。 System.gc()
调用只是建议VM进行垃圾回收。
thread
不是gc的目标。除非完成运行,否则不会清除线程。
通常来说,如果仍然引用其他对象,则认为它们是活的。
答案 4 :(得分:0)
您永远不应调用System.gc。内存不足时,系统会为您调用。
在Java中,GC在称为“标记和扫描”的系统上工作。该算法的工作原理如下
(这是一种简化,现代的实现方式类似于这样,但是要复杂得多)。
那么什么是GC根?存储在局部变量中的任何对象仍在作用域,静态变量,JNI引用以及当前正在运行的所有线程中。
因此,不,除非完成运行,否则不会清除线程。这就是线程如此容易产生内存泄漏的原因-只要线程运行,它们所引用的任何对象就无法释放,因为GC根(线程)已经对其进行引用。
但是关系总是从根到其他对象。如果Foo拥有对Bar的引用,则可以删除Foo,而不管Bar是否可以。但是如果不能删除Foo,那么Bar也不能删除。