垃圾收集器不会立即收集完成的线程
Garbage Collector doesn't immediately collect finished thread
简而言之:我有一个线程已完成 运行,但未被垃圾回收。
长:见下面的示例代码:
public void saveSomething() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// heavy memory access (~400 MB), finishes after ~10sec
}
});
thread.start();
}
好的,所以这个线程开始并在一段时间后结束。这段时间使用了很多内存,这没关系。但我的问题是:
线程结束后,内存没有释放
而且我不知道如何确保这一点。
据我所知,不再使用的已完成线程应该被垃圾收集;但这似乎并没有发生在这里:(
查看来自 jvisualVM 的屏幕截图:
1:我通过调用 saveSomething()
触发线程启动
2:线程早就结束了(调试看到的),我在jvisualvm"Perform GC"中按下
如您所见,在我强制执行 GC 之后,一切都按我想要的方式运行。但这必须自动发生。我该怎么做,我做错了什么?
如果您需要更多信息,请询问。
注意:似乎一天后(希望更短)内存恢复正常,也许 GC 只是 "slow" 或者不是很频繁地计时?
垃圾收集不会在线程结束后立即发生。垃圾收集会在它发生时发生。事实上,当您通过 visualvm 强制垃圾收集时,线程及其资源被正确收集意味着一切正常。如果您等待足够长的时间或做更多消耗堆的事情,那么最终您的线程将被 GC。
编辑
唯一需要注意的是 运行 线程,即使没有引用,也不会被垃圾回收。
通常,在以下情况下,对象在 Java 中符合垃圾回收条件:
1) 该对象的所有引用显式设置为空,例如对象=空
2) 对象在块内创建,一旦控件退出该块,引用就会超出范围。
3) 父对象设置为空,如果一个对象持有另一个对象的引用,并且当您将容器对象的引用设置为空时,子对象或包含的对象将自动符合垃圾回收条件。
4) 如果一个对象只有通过 WeakHashMap 的活弱引用,它将有资格进行垃圾收集。
有System.gc() 和Runtime.gc() 等方法用于向JVM 发送垃圾收集请求,但不能保证一定会发生垃圾收集。
回答你的问题。你需要强制垃圾回收。
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。
JVM 完全有可能不需要在线程结束后执行 GC,因为它有足够的空闲堆。
它只会在需要释放内存时执行 GC。
就个人而言,我不会担心。只要你知道GC发生时内存会被清除就可以了。
如果你真的想强制执行它,你必须明确地执行 System.GC - 在线程完成后,或者在 运行() 方法的最后一行(假设在那个阶段没有对大数据的进一步引用)。
会不会是你问错问题了?我的意思是:没有进行垃圾收集这一事实对您来说是个问题吗?"immediately"?
我很确定 - 当您启动另一个需要大量内存的线程时,GC 将自行启动。
如果 "delay" 实际上对您来说是个问题,您可以考虑执行 "very deep dive" 以了解 GC 在您的 JVM 版本中的实际工作方式;然后开始使用现有的许多命令行选项来根据您的需要微调 GC 行为。
但是如果 "delay" 释放内存(给其他 Java 对象)不是问题;那就不要开始修复它了。
同意前面的回答。该程序演示了 "problem" 并非特定于线程创建这一事实。一般情况下,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
可用内存是浪费内存。 如果您有 2 GB 的可用 RAM,那么您也可以少 2 GB。释放内存本身并没有好处,它不会自动加快速度。
垃圾收集不是免费的。实际上它可能 CPU 非常密集。它发生的次数越少越好(从 CPU 负载的角度来看)。
考虑到这一点,您通常不希望垃圾收集器在线程结束时立即启动。如果您此刻不需要更多 RAM,那么就没有必要在 GC 上消耗 CPU 个周期。大多数时候,您可以信任 JVM 在需要时 运行 GC。
好处:未被程序使用的 RAM 可被您的 OS 用作缓存。在 Windows 上,这部分 RAM 显示为空闲,在 Linux 中显示为已使用,但实际上介于两者之间。缓存不是必需的,但它可以提高性能。 OS 将自动管理它:当有更多空闲 RAM 可用时,可以为缓存分配更多内存,当内存不足时,缓存大小会减小。因此,有时拥有更多空闲 RAM 对性能有好处,但大多数时候您不必担心。
简而言之:我有一个线程已完成 运行,但未被垃圾回收。
长:见下面的示例代码:
public void saveSomething() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// heavy memory access (~400 MB), finishes after ~10sec
}
});
thread.start();
}
好的,所以这个线程开始并在一段时间后结束。这段时间使用了很多内存,这没关系。但我的问题是:
线程结束后,内存没有释放
而且我不知道如何确保这一点。 据我所知,不再使用的已完成线程应该被垃圾收集;但这似乎并没有发生在这里:(
查看来自 jvisualVM 的屏幕截图:
1:我通过调用 saveSomething()
2:线程早就结束了(调试看到的),我在jvisualvm"Perform GC"中按下
如您所见,在我强制执行 GC 之后,一切都按我想要的方式运行。但这必须自动发生。我该怎么做,我做错了什么?
如果您需要更多信息,请询问。 注意:似乎一天后(希望更短)内存恢复正常,也许 GC 只是 "slow" 或者不是很频繁地计时?
垃圾收集不会在线程结束后立即发生。垃圾收集会在它发生时发生。事实上,当您通过 visualvm 强制垃圾收集时,线程及其资源被正确收集意味着一切正常。如果您等待足够长的时间或做更多消耗堆的事情,那么最终您的线程将被 GC。
编辑 唯一需要注意的是 运行 线程,即使没有引用,也不会被垃圾回收。
通常,在以下情况下,对象在 Java 中符合垃圾回收条件:
1) 该对象的所有引用显式设置为空,例如对象=空
2) 对象在块内创建,一旦控件退出该块,引用就会超出范围。
3) 父对象设置为空,如果一个对象持有另一个对象的引用,并且当您将容器对象的引用设置为空时,子对象或包含的对象将自动符合垃圾回收条件。
4) 如果一个对象只有通过 WeakHashMap 的活弱引用,它将有资格进行垃圾收集。
有System.gc() 和Runtime.gc() 等方法用于向JVM 发送垃圾收集请求,但不能保证一定会发生垃圾收集。
回答你的问题。你需要强制垃圾回收。
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。
JVM 完全有可能不需要在线程结束后执行 GC,因为它有足够的空闲堆。
它只会在需要释放内存时执行 GC。
就个人而言,我不会担心。只要你知道GC发生时内存会被清除就可以了。
如果你真的想强制执行它,你必须明确地执行 System.GC - 在线程完成后,或者在 运行() 方法的最后一行(假设在那个阶段没有对大数据的进一步引用)。
会不会是你问错问题了?我的意思是:没有进行垃圾收集这一事实对您来说是个问题吗?"immediately"?
我很确定 - 当您启动另一个需要大量内存的线程时,GC 将自行启动。
如果 "delay" 实际上对您来说是个问题,您可以考虑执行 "very deep dive" 以了解 GC 在您的 JVM 版本中的实际工作方式;然后开始使用现有的许多命令行选项来根据您的需要微调 GC 行为。
但是如果 "delay" 释放内存(给其他 Java 对象)不是问题;那就不要开始修复它了。
同意前面的回答。该程序演示了 "problem" 并非特定于线程创建这一事实。一般情况下,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
可用内存是浪费内存。 如果您有 2 GB 的可用 RAM,那么您也可以少 2 GB。释放内存本身并没有好处,它不会自动加快速度。
垃圾收集不是免费的。实际上它可能 CPU 非常密集。它发生的次数越少越好(从 CPU 负载的角度来看)。
考虑到这一点,您通常不希望垃圾收集器在线程结束时立即启动。如果您此刻不需要更多 RAM,那么就没有必要在 GC 上消耗 CPU 个周期。大多数时候,您可以信任 JVM 在需要时 运行 GC。
好处:未被程序使用的 RAM 可被您的 OS 用作缓存。在 Windows 上,这部分 RAM 显示为空闲,在 Linux 中显示为已使用,但实际上介于两者之间。缓存不是必需的,但它可以提高性能。 OS 将自动管理它:当有更多空闲 RAM 可用时,可以为缓存分配更多内存,当内存不足时,缓存大小会减小。因此,有时拥有更多空闲 RAM 对性能有好处,但大多数时候您不必担心。