垃圾回收

对于一般Java程序员开发的过程中,不需要考虑垃圾回收。

  • 如何判定对象为垃圾对象;
    1. 引用计数法
    2. 可达性分析法
  • 如何回收垃圾对象;
    1. 回收策略(标记清除、复制、标记整理、分带收集算法)
    2. 常见的垃圾回收器(Serial、Parnew、Cms、G1)
  • 何时回收垃圾对象

判定垃圾对象

引用计数算法

在对象中添加一个引用计数器,当有地方引用这个对象的时候,引用计数器的值就加1,当引用失效的时候(变量记为null),计数器的值就减1。但Java虚拟机中没有使用这种算法,这是由于如果堆内的对象之间相互引用,就始终不会发生计数器-1,那么就不会回收。

测试:两个对象相互引用

 

 

打印垃圾回收简易信息的参数:-verbose:gc
打印详细:-verbose:gc -XX:+PrintGCDetails
输出:[GC (System.gc()) [PSYoungGen: 22476K->680K(38400K)] 42956K->21168K(125952K), 0.0008355 secs]可以看出对象被回收,因此Java不使用引用计数算法。

可达性分析法

此算法的核心思想:通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为“引用链”,当一个对象到GC Roots没有任何的引用链相连时(从GC Roots)到这个对象不可达)时,证明此对象不可用。

 

 

可作为GC Roots的对象:

  1. 虚拟机栈
  2. 方法区的类属性所引用的对象
  3. 方法区中常量所引用的对象
  4. 本地方法栈中引用的对象

垃圾回收算法

标记清除算法

先标记出要回收的对象(一般使用可达性分析算法),再去清除,但会有效率问题和空间问题:标记的空间被清除后,会造成我的内存中出现越来越多的不连续空间,当要分配一个大对象的时候,在进行寻址的要花费很多时间,可能会再一次触发垃圾回收。

复制算法

堆:

    • 新生代
      Eden 伊甸园
        Survivor 存活期
        Tenured Gen 老年区
    • 老年代

复制算法是将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,浪费较大。复制算法的执行过程如下图所示:

 

 

现在的商业虚拟机都采用这种收集算法来回收新生代,IBM公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性的复制到另外一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性的复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存为整个新生代容量的90%(80%+10%),只有10%的内存会被“浪费”。

 

posted @ 2021-11-03 17:10  鹏了个鹏鹏  阅读(92)  评论(0)    收藏  举报