JVM的内存回收及常见算法小结
作者:程序员·小李
什么样的对象应该被回收?
某个对象不再被栈直接或间接地引用,此时就应该被回收了。
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 |
到此这篇关于JVM的内存回收及常见算法的文章就介绍到这了,更多相关JVM的内存回收内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!