java - 垃圾回收器何时运行
当JVM没有足够的内存空间运行时,垃圾收集器将会运行并删除不必要的对象以释放内存。不必要的对象是指没有其他引用(地址)指向它们的对象。
一个对象有4种主要的垃圾收集资格:
1. 空引用
当对象的引用变量被赋值为null
时,垃圾收集器可以删除该对象。
A a = new A(); a = null;
2. 重新赋值
当另一个对象被赋值给一个对象的引用变量时,垃圾收集器可以删除旧的被引用对象。
A a = new A(100); a = new A(200);
3. 局部作用域
如果一个对象在一个块内创建,那么在该块之外,该对象将会被垃圾收集。
if(condition){ A a = new A(); }
4. 隔离
一个对象可以包含对另一个对象的引用,但是在栈中必须至少有一个引用变量指向这些对象,否则所有这些对象都将有资格被垃圾收集。
class A{ A r; A(int i){ //something } } A a1 = new A(100); a1.r = new A(101); a1.r.r = new A(102); a1.r.r.r = a1; a1 = null; //所有对象都有资格被垃圾收集
Java中的垃圾回收器(Garbage Collector)是负责自动回收不再使用的内存空间的机制。但是,我们无法准确地确定垃圾回收器何时运行。垃圾回收器的运行方式取决于程序的JIT编译方式以及特定垃圾回收器所遵循的算法。此外,垃圾回收器的运行还依赖于客户端机器上的Java虚拟机以及特定时间点的可用虚拟内存。
为了检查垃圾回收器的行为,我们可以尝试运行以下小程序:
public class GCTest { final int NELEMS = 50000; void eatMemory() { int[] intArray = new int[NELEMS]; for (int i=0; i
可能的输出结果(在您的情况下可能会有所不同):
free memory before creating array: 4054912 free memory after creating array: 3852496 free memory after running gc(): 4064184
以上输出结果显示了在创建数组之前、创建数组之后以及运行垃圾回收器之后的可用内存量。我们也可以通过运行垃圾回收器来检查可用内存的变化。
更多关于垃圾回收器的信息,请参考此链接:[http://www.devdaily.com/java/edu/pj/pj010008/](http://www.devdaily.com/java/edu/pj/pj010008/)
输出结果为:free memory before creating array: 263777600 free memory after creating array: 263577584 free memory after running gc(): 14174392。为什么运行垃圾回收器之后显示的内存较少?
Java垃圾回收器何时运行的原因是,它在确定需要运行时才运行。一种常见的策略是,在分代垃圾回收器中,当分配第0代内存失败时运行回收器。也就是说,每当分配一块小的内存块时(大的内存块通常直接放入“较老”的代中),系统会检查第0代堆中是否有足够的空闲空间,如果没有,则运行垃圾回收器以释放空间以使分配成功。
然后,旧数据被移动到第1代堆中,在那里空间用尽后,GC会在那个堆上运行一次垃圾回收,将最长时间存在的数据升级到第2代堆,依此类推。因此,垃圾回收器不仅仅是“运行”。它可能仅在第0代堆上运行(大多数回收将只做这个),或者只有在需要释放大量内存时才会检查每个代(这通常是相当少见的)。
但这远非唯一的策略。并发垃圾回收器在程序运行时后台进行清理。一些垃圾回收器可能在每次内存分配时都运行。增量收集器可以这样做,即在每次内存分配时扫描一些对象。
垃圾回收器的整个目的是,它应该在不需要用户输入的情况下完成自己的工作。因此,通常情况下,你不能预测垃圾回收器何时运行,并且不应该这样做。
其他JVM当然可以选择任何喜欢的策略。
关于Java和分代垃圾回收的上述部分是不正确的。Java从1.2版本开始使用分代收集器,它具有更好的碎片整理行为。Java 6中引入了Garbage-First垃圾回收器(G1),可在Java 6u14中使用。根据发布1.6.0_14版本文章中的说法:“它默认情况下未启用。并行收集器仍然是默认的GC,并且是最适合常见家庭使用的GC。G1是并发收集器的替代品。它旨在更可预测,并通过内存区域设计实现快速分配。”
Java自从JDK 1.3版本以来就有分代垃圾回收器。分代垃圾回收器意味着GC仅检查某些对象是否为垃圾。它经常检查任何新创建的对象(第0代)是否可以释放。如果一个对象存活足够长时间以达到某个阈值,那么它将被放入第1代中,这意味着GC在正常快速垃圾收集期间不会检查它。偶尔,GC会查看第1代对象,看看是否有任何可以释放为垃圾的对象,而更少见的是,它会查看第2代对象(直到GC使用的代数为止)。
这只是一种启发式的实现,即“如果一个对象刚创建,很可能很快就会成为垃圾,所以每当我们需要释放内存时,这些应该是我们首先检查的对象。但如果它存在很长时间,它可能会继续存在很长时间。因此,经常检查它将是低效的”。
R Lobato:这个问题(虽然很老,但是可以理解)是典型的错误思维,即“垃圾必须被删除”。分代垃圾回收器将所有存活对象从一个内存空间移动到另一个内存空间,这意味着剩下的东西要么是未使用的内存,要么是垃圾,不需要任何额外的操作,因为从整体上看,内存空间现在是空闲的,根据定义。
较旧的代不仅遍历频率较低,JVM还会切换策略。虽然年轻代需要遍历所有年轻引用,JVM将跟踪较旧代的更改,因为旧对象变化较少,所以GC也可以跳过未自上次主要GC更改的旧对象区域,因此仍然引用与之前相同的对象。