Java底层知识之JVM详细研究二(垃圾收集器以及内存分配策略)

垃圾回收

  垃圾回收的三个问题:

    哪些内存需要回收?

    什么时候回收?

    如何回收?

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.如何回收

  标记-清除算法:

    先标记要回收的对象,然后一次性回收。

    缺点:效率不高,会导致大量的空间碎片。

  复制算法:

    将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还活着的对象存放到另一块上去。然后再清楚原来的空间。

    优点:高效,只需要移动堆顶指针即可。

    缺点:将可用内存压缩了一半!

  标记-整理算法

    就是对标记-清除法做了改进,标记后不直接清楚而是把剩余的向前整理,避免碎片

垃圾回收完

-----------------------------------------------------

Github:

https://github.com/RainFool
原文地址:https://www.cnblogs.com/RainFool/p/3913673.html