垃圾回收
垃圾回收的三个问题:
哪些内存需要回收?
什么时候回收?
如何回收?
1.哪些对象需要回收?
判断对象是否存活的办法:
引用计数算法:给对象中添加一个引用计数器,有一个地方引用就+1,引用失效就-1.只要计数器为0则对象已死。
优点:简单易实现;
缺点:无法解决对象之间相互引用的问题。(JVM也因为此种原因没有使用它)
根搜索算法:
通过选取出一个GC Roots对象,已它作为起始点,如果对象不可达,则对象已死。
GC Roots对象:
-
-
-
-
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
-
-
-
引用的几种形式:
-
-
-
-
- 强引用:最普遍的引用,例如Object obj = new Object();之类
- 软引用:有用但并非必须的对象。系统在内存溢出发生之前,会尝试将这些对象进行回收,如果回收完后还是内存不足,才会OOM
- 弱引用:只能存活到下一次垃圾回收发生前的对象
- 虚引用:不能通过虚引用使用一个对象,其存在的意义仅仅在于对象回收时得到一个通知
-
-
-
2.什么时候回收
当确定好了GC Roots,而且根搜索算法不可达的对象也不是非死不可,它还有一次拯救自己的机会(垃圾回收一个对象的过程):
如果对象没有与GC Roots相连的引用,它将会被第一次标记并进行筛选,
筛选的条件是此对象是否由必要执行finalize()方法。当对象没有覆盖finalize()方法或者已经被虚拟机调用过,虚拟机将这种情况视为“没有必要执行”。
如果被判定为“有必要执行”,那么这个对象会放到一个名为“F-Queue”的队列之中,并在随后由一条由虚拟机自动建立、低优先级的Finalizer线程去执行。(这里便是人们常说的虚拟机不保证执 行,因为在一个对象的finalize方法中可能有很多极端情况,例如死循环,这样会导致队列的执行缓慢,甚至垃圾回收机制崩溃。)稍后GC将对F-Queue中的对象进行第二次小规模标记,这时候如 果对象想要拯救自己,就只要在finalize()中将自己赋值给某个根可达的变量就行了。如果这样的话,对象将在第二次标记的时候被移除出“即将回收”的集合。
(注意!)一个对象的finalize方法只会被调用一次,GC不会给任何对象第二次犯错的机会。而且,finalize方法没有任何存在的价值。
3.如何回收
标记-清除算法:
先标记要回收的对象,然后一次性回收。
缺点:效率不高,会导致大量的空间碎片。
复制算法:
将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还活着的对象存放到另一块上去。然后再清楚原来的空间。
优点:高效,只需要移动堆顶指针即可。
缺点:将可用内存压缩了一半!
标记-整理算法
就是对标记-清除法做了改进,标记后不直接清楚而是把剩余的向前整理,避免碎片
垃圾回收完