怎么判断一个对象是垃圾
判断算法
- 引用计数法
当别的对象引用该对象时,该对象内部计数器会加1,失效时计数器会减1,当计数器为0时,对象就会被回收
当栈中的obj引用堆中的obj1时,obj1内部计数器会加1,
obj1引用obj2时,obj2内部计数器也会加1,而同时obj2引用obj1,obj1内部计数器将会再加1。
引用计数器的问题:
- 哪怕是栈里面的obj对堆里面obj1进行了释放,那么obj1的计数器还是不为零,如果两个对象相互依赖,导致内部的计数器用于不会0,导致永远不能被回收
- 可达性分析
概念:可达性分析有一个 GC ROOT作为对象起始点,从这个节点开始向下搜索,搜索走过的路径成为引用链路,当一个对象没有任何GC ROOT引用链路时,则证明是不可达的,可以判断为该对象可以被回收
由于顺着GC ROOT搜索可以找到person和car,所以这两个对象是可达的,不能回收,computer虽然跟mouse有关联,但是没有GC ROOT指向,所以是不可达的,是可以回收的对象
哪些可以成为GC ROOT呢
1、虚拟机栈的本地变量表:局部变量里面肯定是有线程在执行,所以不能回收
例如:obj(GC ROOT) = 引用
2、静态(static)成员
3、常量
4、本地方法栈(C++)跟虚拟机栈类似
5、类加载 classloader:会一直在JVM虚拟机中
垃圾收集算法
- 标记清除算法
两步:标记、清除- 标记:根据可达性分析,找出内存中需要回收的对象,把这些对象标记出来
- 清除:清除掉被标记需要回收的对象,释放出对应的内存空间
- 标记:根据可达性分析,找出内存中需要回收的对象,把这些对象标记出来
缺点
标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序
运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次
垃圾收集动作。
(1)标记和清除两个过程都比较耗时,效率不高
(2)会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中
需要分配较大对象时,无 法找到足够的连续内存而不得不提前触发另一次垃圾
收集动作。
- 复制回收算法
概念:将可用的内存按容量划分为大小相同的两块,每次只是用其中的一块。当发生垃圾回收时,将还存活着的对象复制到另一块上面,然后把已经使用过的内存空间一次清理掉,类似于堆中的S区
如果发生了GC,会把存活的对象复制到另一块区域,回收其他对象
并且将存活的对象进行排序,形成连续的地址空间,从而不产生空间碎片
优点:
-高效
-不会产生空间碎片
缺点:
-内存只能使用原来的一半
- 标记整理算法
概念:该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。
两步:标记、整理- 标记:标记过程仍然与"标记-清除"算法一样
- 整理:移动所有存活的对象,且按照内存地址依次排列,然后将存活对象的末端内存地址以后的内存全部回收
- 标记:标记过程仍然与"标记-清除"算法一样
优点:不会产生空间碎片
- 分代收集算法
既然上面介绍了3中垃圾收集算法,那么在堆内存中到底用哪一个呢?
Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
Old区:标记清除或标记整理(Old区对象存活时间比较长,复制来复制去资源开销大,所以选择标记清除或标记整理)