垃圾回收
-
什么场景用什么垃圾回收策略
- 对内存要求高的时候:想办法提高对象的回收效率,多回收掉一些对象,腾出更多内存
- 在cpu使用率高的情况下:降低高并发时垃圾回收的频率,让CPU更多地去执行你的业务而不是垃圾回收
-
对象什么时候被回收
- 引用计数法
- 可达性分析
垃圾什么时候回收呢?
-
引用计数法
-
当存在对这个对象的引用时 计数加1 当计数为0时 对象被销毁
-
基于图的实现 邻接表 有边则为1
-
使用广度有限算法来回收内存
-
如果有环的话,如果发现节点在visited中存在,就是有环,可以手动删除一条边,将计数减1 再次从计数为0 的节点开始BFS回收
-
缺点
- 基于扫描的引用计数策略性能太慢 需要遍历
- 每个对象多维护一个引用
- GC和另外的线程的维护会产生竞争条件,会产生STOP the world
-
-
可达性分析算法
-
对象没有引用链和GcRoots连接 if(对象有必要执行finalize()){ 将对象放入F-Queue,虚拟机自动创建低优先级线程finalizer执行对象的finaliz() if(对象在finalize()中重新建立连接){ 从F-Queue移除 不回收 }else{ 回收 } }else{ //对象未重写finalize()或虚拟机已经调用过finalize() 回收 }-
在栈中分配的对象存入一个集合中(root set)
-
每次遍历Root set 中的对象 追溯到Heap中的对象进行分析
-
分成两个阶段标记(mark)和(Sweep)
-
Mark负责标记在使用的对象
-
Sweep负责删除不被使用的对象
-
那些对象可以当做CGroots
- 方法区静态属性引用的对象 全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。
- 方法区常量池引用的对象 也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。
- 方法栈中栈帧本地变量表引用的对象 属于执行上下文中的对象,线程在执行方法时,会将方法打包成一个栈帧入栈执行,方法里用到的局部变量会存放到栈帧的本地变量表中。只要方法还在运行,还没出栈,就意味这本地变量表的对象还会被访问,GC就不应该回收,所以这一类对象也可作为GC Roots。
- JNI本地方法栈中引用的对象 和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。
- 被同步锁持有的对象 被synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效了嘛。
-
引用
-
强引用
- new object() 只要强引用在 永远不会回收被引用的对象
-
软引用
- 如SoftReference sr = new SofrReference<>(“hello”)
- 用来描述一些有用但非必须得对象
- 软引用 关联的对象,只有在内存不足的时候才会回收
- 可以用来做缓存
-
弱引用
- 如weakReference sr = new weakReference<>(“hello”)
- 弱引用也是用来描述非必须对象的
- 无论内存是否充足,都会回收被弱引用关联的对象
-
虚引用
- ReferenceQueue queue = new ReferenceQueue<>();
- PhantomReference pr = new PhantomReference<>(“hello”,queue);
- 不影响对象的生命周期,如果一个对象只有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾收集器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动,必须和引用队列(ReferenceQueue)配合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动
-
-
三色标记法
- 白色:尚未访问过
- 黑色:本对象以及访问过,本对象引用到的其他对象也全部访问过了
- 灰色:本对象已经访问过,但是本对象引用到的其他对象尚未完全访问完,全部访问后会变为黑色
-
浮动垃圾
- 三色标记法是并行的 在遍历时 灰色对象与黑色对象断开链接,但还是会进行遍历 , 多出来的本因该销毁的对象逃过本轮GC下次GC时再回收
-
漏标(对象本因该存活但还是销毁)
- 在遍历灰色对象时,1.将灰色对象指向白色对象的链接销毁,又2.新增了一条黑色对象指向此白色对象,此白色对象本因该存活但是会被销毁
- 必须满足以上两个条件才会出现对象销毁但还是被使用
-
解决方案
-
原始快照
- 破坏第一个条件 当灰色对象要删除白色对象的引用关系时,将这个要删除的引用记录下来,并发扫描结束后,在将这些记录重新扫描一次
-
增量更新
- 破坏第二条 在新增一条引用时,将该记录保存。
-
-