什么样的对象应该被回收?
某个对象不再被栈直接或间接地引用,此时就应该被回收了。
o被指向null的时候,new Object()创建的对象就不在被栈引用了,可以被回收。
p1和personList均不再指向第一个Person对象的时候,第一个Person对象、list对象可以被回收。
经历前面的几个阶段,内存引用是这样的情况。
p1 = null后,p1曾经指向的对象虽然不再被栈直接引用,但是仍然间接通过persons引用。
此时p1指向的对象和list指向的对象都可以被回收了。
怎么样确定哪些需要回收?
对我们仍然需要使用的对象进行标记,回收没有标记的对象。
以此为例,如何进行标记的呢?
垃圾回收进行之前,所有对象的标记位是0
如果仅仅标记栈直接引用的对象,p1就会被回收,但是p1间接被list引用,因此也被标记为1
标记算法
-Stop the World(GC Root可达性算法)
在进行上面的标记过程的时候,如果有新的对象被创建,而刚好被标记过程错过的时候,就可能错误地把有用的对象给回收掉,因为标记位是0.因此,Stop the World正如其名,将应用的核心线程停掉,开始专心标记。
-引用计数法
对对象进行引用数量的标记,没有引用的对象标记是0,有引用的对象标记是引用数量。清除标记为0的对象即可。但是引用计数法有个问题,无法解决循环引用的问题,导致内存泄露。
这里声明一个对象,内部包含一个跟自己一个类型的成员变量。
在执行第五行之前,两个对象的引用计数均为2,各自引用,加上n1和n2.
执行完第五六行以后,按道理,栈上已经不在引用这两个对象,可以被回收了,但是因为n1和n2相互引用,导致引用计数为1,无法正常回收。
清除算法
一般清除算法:直接将未标记的对象清理掉
经过清理,未标记的对象被回收。
但是存在内存碎片化的问题,只能从间隙处继续分配内存,
存在内存不连续的问题,内存空间浪费严重,容易oom
清除-整理算法:先清除没有标记的对象,然后将剩余的存活对象进行整理,让内存空间更加连续。
就是代价比较高,几乎需要移动所有的对象。
复制-清除算法:将活跃的对象复制到另一个内存区域,然后清除当前区域的所有对象!
完成复制后,清除原有的区域
这种算法的弊端就是需要更多的内存空间。
常见的GC类型
GC类别 | 新生代垃圾回收 | 老年代垃圾回收 | 特点 |
Serial GC | 标记-复制&清除 | 标记-清除&整理 | Stop the World 使用单个线程处理 适合小应用 |
Parallel GC | Java 8默认垃圾回收器 Stop the World 使用多个线程处理 | ||
CMS GC | 标记-复制&清除 (Stop the World) | 接近并行的标记-清除 初始标记->并发标记 -> 并发预清理->可中断预清理->重新标记->并发清除->并发重置 | |
G1 GC |
| -XX:UseStringDeduplication | |
Z GC | 不在维护映射,而是对象上保持一个标记来表示活跃对象 仅支持64位系统 采用重定位解决内存碎片化问题 | Java 15 |