JVM系列见解之垃圾回收概念和算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37774696/article/details/79143236

1.认识垃圾回收

说到垃圾回收,首先要说明垃圾是什么,类比于生活中垃圾,特指在内存中不会再被引用的对象,而回收相当于将垃圾桶“倒掉”,我们必须经常清理房间里的垃圾。内存空间也是一样,需要对一些不再使用的对象进行清理,以释放更多空余空间。相信学过C语言的或者c++的同学们都很清楚,我们需要手工进行垃圾回收:new关键字申请内存,delete关键字进行释放内存。在java中,强大的jvm帮我们实现了垃圾的回收。不过作为程序员,我们必须了解jvm垃圾回收的知识,才能更好地利用jvm进行高效率的回收垃圾,从而提高程序整体的性能。

2哪些对象可以回收

2.1引用计数器法

每个对象创建时,会分配一个引用计数器。接下来每次对这个对象每次引用,对应的计数器就会加1。而每次将引用置为null,即引用失效时,计数器会执行减1。只要这个引用的计数器值为0,就可以当做是垃圾,可以被回收。
说到这里,相信大家都觉得这个原理很容易实现。可事实上我们的jvm并不是使用这种方法判断垃圾,这个方法实现有一个致命的问题就是:当两个引用只是相互指向对方时,两者的计数器值都为1,所以没有其他任何引用能指向他们,他们依然不会被视为垃圾,这显然是很不合理的。

2.2根搜索

讲这个之前,我先跟大家说说根对象集合由什么组成。
根对象集合包含:

  • 常量池的静态属性引用对象
  • 方法区的静态对象引用(一般指的是由fianl修饰的对象)
  • 本地方法栈引用对象
  • java栈的引用对象
  • 与一个类对应的唯一数据类型的class对象
    接下来重点来了,根搜索算法就是以根对象集合为起点,按照从上到下的方式搜索被根对象集合直接或者间接锁连接的目标对象是否可达(使用根搜索算法后,内存中存活的对象都能通过根对象集合直接或者间接找到),如果对象不可达,就意味着对象已经死亡,可将其标记为垃圾对象,可以被回收。
    这里写图片描述

3 经典GC算法

  • 引用计数法,也就是第一部分所说的引用计数器内容,这里不再阐述
  • 标记清除法:在标记阶段,首先通过根节点,标记所有能从根节点可达的对象。在清除阶段,会将没有标记的对象,视为垃圾对象,将其清理。标记清除算法最大的问题是:会造成内存碎片,所以在对象对堆空间的分配时,连续的分配空间效率会高于不连续分配空间的效率。
  • 复制算法:就是将原有的内存空间分为两块,每次只用其中一块,在垃圾回收的时候,将还在内存中存活的对象复制到另外一块还未使用的内存空间,之后再清除使用的那块内存空间的内容,两块空间互换角色,完成了垃圾的回收。复制算法的问题主要是加入存活的对象过多,复制开销会比较大,不过复制算法可以确保没有内存碎片,一般会在新生代使用,因为新生代区可存活的对象比较少。

新生代:存放年轻对象的堆空间,指的是刚刚创建的对象或者经历过GC的次数没有经过jvm设定的阀值,不过这个大于这个阀值只能是一个充分非必要的条件,因为对象是否为新生代与其大小也有关系,有些大对象能直接跳过新生代进入老年代。
老年代:存放老年对象的堆空间。一般指的是经历过多次GC后存活下来的对象。

  • 标记压缩算法:在标记清除算法的基础上做了一些优化,标记阶段,也是先从根节点开始,对所有可达的节点做标记。不过在清除阶段,并非只是简单清除没有标记的对象,而是先将可达的对象压缩放到内存的一端,以此为边界,将边界外的内存空间进行清理。简单来说标记压缩相当于一次标记清除加上一次内存碎片的整理,一般在老年代使用。
  • 分代算法:上面的所讲的算法各自都有相应的优缺点,彼此都无法代替。如何选择合适GC算法是一个很重要的选择。分代算法与其说是算法,不如说成是一种思想。它结合了以上算法特点以及新生代和老年代的空间对象的特点,分别采用不同的GC算法,以提高垃圾回收的效率。
  • 分区算法:将内存空间划分成连续的不同小区间,每个小区间都独立回收和控使用,最大的好处就是可以控制·一次回收多少区间垃圾。

接下来将会发表更多的JVM方面的知识,希望能和大家一起进步。如果文章中有说得不好的地方希望各位猿友指点下。 -

猜你喜欢

转载自blog.csdn.net/m0_37774696/article/details/79143236