垃圾回收概述
1.java是支持自动垃圾回收的,
2.在jvm中堆和方法区需要进行垃圾回收,
3.一般没有被引用指向的对象成为垃圾,如下图 s在运行过程中被赋值为null 那么“aaa”就成为没有引用指向的对象
4.垃圾回收是必要的 如果不及时清理这些垃圾对象,会导致内存溢出和内存泄漏.
5.再回收过程中还可以对内存碎片进行整理 以便分配内存给其他对象(有些对象必须连续空间存储 如数组)
内存溢出和内存泄漏:
内存溢出(OOM 对 OutofMemoryError):没有多余的内存去存储被创建的对象但经过垃圾回收后,内存中仍然无法存储新创建的对象,就是内存不够用溢出.导致程序崩溃
内存泄漏:只有对象不会再被程序用到了,但是GC又不能回收他们的情况才叫内存泄漏。比如IO流 jdbc连接没有close关闭,单例模式和一些生命周期很长的对象, 虽然这些对象已经不用了,但是垃圾回收器不能将其判定为垃圾,这些对象就会默默的占用的内存,称为内存泄漏,大量的此类对象存在,也是导致内存溢出的原因. 尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可 用内存就会被逐步蚕食,直至耗尽所有内存,最终出现 OutofMemory 异常, 导致程序崩溃
垃圾回收机制:
java垃圾回收机制是 自动内存管理 无需开发人员手动参与内存的分配与回收,这样降低内存泄漏和 内存溢出的风险. 自动内存管理机制,将程序员从繁重的内存管理中释放出来,可以更专心地专注于业务开发
垃圾回收回相关算法
1.标记阶段算法
作用:选出是垃圾的对象进行标记 该对象不再被任何引用指向时就可以宣判为垃圾对象。
相关的标记算法:引用计数算法和可达性分析算法
引用计数算法(已经不被使用)
String s1 = new String("aaa");
String s2 = s1; //有两个引用变量指向aaa对象
s2 = null; //“aaa”引用数量-1
s1 = null; //“aaa” 引用数量为0 被标记为垃圾对象
该算法的优点:
实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。
缺点:
循环引用:在A对象中引用了B对象,在B对象中引用了A对象 除此之外没有别的外部的引用指向他们俩 但由于A B对象的计数器都不为0 所以不能被判定为垃圾,不能被回收 会导致内存泄漏。
如下图p断开对A的引用 但由于A B C 相互引用 “rc”(计数器)都不为0 所以 A B C都不能被标记为垃圾 不能被回收导致内存泄漏
可达性分析算法:
1.在虚拟机栈中被使用的.
2.在方法中存储的静态成员指向的对象
3.作为同步锁使用的 synchronized
4.在虚拟机内部使用的对象
对象的 finalization 机制:
当一个对象被标记为垃圾后,在真正被回收之前,会调用一次Object类中finalize()方法(相当于电视剧中的刀下留人).判断是否还有逻辑需要进行处理比如释放资源之类的。finalize()方法只会被调用一次(刀下留人只能喊一次 喊多了不顶用了) .
注意:自己不要在程序中调用finalize(),留给垃圾回收器调用.有以下三种原因:
1.在finalize()时可能会让对象复活。
2.finalize()方法的执行时间是没有限度的,它完全室友GC线程决定。
3.自己重写调用的finalize()方法 可能性能很差 从而影响GC的性能。
有了finalization机制的存在,在虚拟机中把对象状态分为3种:
1.可触及的 不是垃圾,与根对象连接的
2.可复活的 判定为垃圾了,但是还没有调用finalize(),(在finalize()中对象可能会复活)
3.不可触及的: 判定为垃圾了,finalize()也被执行过了(刀下留人喊过了),这种就是必须被回收的对象 也不会再次调用finalize()方法
2.回收阶段算法
经过标记阶段 确认垃圾对象后 接下来就是收集垃圾对象的阶段,垃圾收集阶段有以下三种常见的收集算法:
1.标记-复制算法
将内存分为大小相等的两份空间(也就是幸存者1区和幸存者2去), 把当前使用的空间中存活的对象 复制到另一个空间中, 将正在使用的空间中垃圾对象全部清除.
如下图 将A中存活的对象 全部复制到B中,然后将A全部清除
优点: 复制后会保证空间的连续性,减少内存碎片。
缺点: 如果需要复制的对象数量多,效率会很低.因为需要两倍的内存空间所以内存开销大
适用场景: 适用于存活对象少 比如新生代就比较适合使用标记复制算法 进行垃圾回收
2.标记-清除算法
此处的清除不是真正的把垃圾对象清除掉,而是将垃圾对象地址维护到一个空闲列表中,后面有新对象到来时,覆盖掉垃圾对象即可.
优点:实现简单易懂,
缺点:因为要维护空闲列表,所以效率低 而且会产生内存碎片。
3.标记-压缩算法
复制算法的高效,是建立在存活对象少、垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老年代,存活对象很多,如果依旧使用复制算法,复制成本很高。而标记清除算法在老年代不仅效率不高而且还会产生内存碎片,所有有了标记—压缩算法。
标记压缩算法的执行过程:
第一阶段和标记清除一样,对对象进行标记。
第二阶段把存活的对象压缩到内存的一端,按顺序排放,然后清理边界外的所有空间。
优点:
整理的空间没有内存碎片。
不需要像复制算法那样,运用多余的空间。
由于随想都是按顺序存放的,所以给新对象分配内存地址时,只需要持有一个内存起始地址即可(与链表相似)
停用户应用程序。即:STW
STW
Stop-the-World,简称 STW,指的是 GC 事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为 STW。
3.回收算法小结
![](https://img-blog.csdnimg.cn/9538e38eac8c40d98a599aae5e767217.png)
4.垃圾回收器
垃圾回收器用来执行 以上的垃圾回收算法
1.垃圾回收器的分类:
按线程分类
单线程垃圾回收器(Serial,Serial old):只有一个线程进行垃圾回收,使用于小型简单的使用场景,垃圾回收时,其他用户线程会暂停
多线程垃圾回收器(Parallel):多线程垃圾回收器内部提供多个线程进行垃圾回收,在多 cpu 情况下大大提升垃 圾回收效率,但同样也是会暂停其他用户线程
按照工作模式:
独占式: 垃圾回收线程执行时,其他线程暂停
并行式: 垃圾回收线程可以和用户线程同时执行(实践上不是真正的同时而是暂停时间短可以看作是同时)
按工作的内存区间分,又可分为年轻代垃圾回收器和老年代垃圾回收器。
2.垃圾回收器性能指标
![](https://img-blog.csdnimg.cn/26c9ef94793b46d58e7129bdfe8adf15.png)
CMS垃圾回收器
CMS(Concurrent Mark Sweep,并发标记清除)收集器是以获取最短回收停顿 时间为目标的收集器(追求低停顿),它在垃圾收集时使得用户线程和 GC 线程并 发执行,因此在垃圾收集过程中用户也不会感到明显的卡顿。
cms回收过程:
初始标记: 独占式的暂停用户线程,Stop The World,仅使用一条初始标记线程对所有与 GC Roots 直接关联的对象进行标记。
并发标记: 垃圾回收线程,与用户线程并发执行。此过程进行可达性分析,标记出所有废弃对象。
重新标记: 独占式的暂停用户线程。Stop The World,使用多条标记线程并发执行,将刚才并发标记过程中新出现的废弃对象标记出来
并发清除: 垃圾回收线程与用户线程并发(同时)执行 进行垃圾对象的清除
优点:
可以作到并发收集
缺点:
三色标记
由于cms有并发执行过程,所以在标记垃圾对象时有不确定性.
所以在标记时,将对象分为3种颜色(3种状态)
黑色: 例如GCRoots 确定是存活的对象
灰色: 在黑色对象中关联的对象,其中还有未扫描完的, 之后还需要再次进行扫描
白色: 与黑色,灰色对象无关联的, 垃圾收集算法不可达的对象
标记过程:
![](https://img-blog.csdnimg.cn/01c6fa55dd784e6e99e96509a168d4cc.png)
![](https://img-blog.csdnimg.cn/f235dfa3680d47c787c656efdcda2d1d.png)
解决错标的问题
![](https://img-blog.csdnimg.cn/1aa5300612574678aa55ffb69e0a7195.png)
G1垃圾收集器
G1(Garbage-First) 垃圾优先 老年区和新生区他都可以搞定
将堆内存各个区又分成较小的多个区域, 对这些个区域进行监测,对某个区域中垃圾数量大的区域优先回收.也是并发收集的.