GC简介

GC 是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java 语言没有提供释放已分配内存的显示操作方法。Java 程序员不用担心内存管理, 因为垃圾收集器会自动进行管理。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收, 程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java 诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题。

GC有很多优点,比如:
开发者无需过问内存管理,可以专注于解决实际问题;
内存泄露发生的概率比较小;
GC的智能算法可以在后台自动的进行内存管理,且这种管理在大多数时候是最佳的;

当然GC也会相应的存在一些缺点:
当垃圾回收发生时,它会影响程序的性能,甚至会暂停程序的执行,整个程序进程会被暂停以等待垃圾回收执行完,对某些应用而言,这可能是无法接受的;
开发者并不能指定何时或使用何种方法执行GC;

image.png
JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代,因此GC按照回收的区域又分为了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)
Minor GC和Full GC的区别:

  • 普通GC(minor GC):只针对新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快
  • 全局GC(major GC or Full GC):指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次Minor GC(但并不是绝对的)Major GC的速度一般要比Minor GC慢上10倍以上

    GC算法

    引用计数法

    引用计数算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为该对象不再被使用,是“垃圾”了。引用计数实现简单,效率高;但是不能解决循环引用问题(A对象引用B对象,B对象又引用A对象,但是A,B对象已不被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。
    image.png

    复制算法

    新生代(年轻代)中使用的Minor GC采用的是复制算法Minor GC会把Eden中的所有活的对象都移到Survivor区域中,如果Survivor区中放不下,那么剩下的活的对象就被移动到Old generation中,也即一旦垃圾收集后,Eden就变成空的了当对象Eden(包括一个Survivor区域,这里假设是From区域)出生后,在经过一次Minor GC后,如果对象还存活,并且能被另一块Survivor区域所容纳(上面已经假设为from区域,这里为to区域,即to区域有足够的内存来存储Eden和From区域中存活的对象),则使用复制算法将这些仍然还存活的对象复制到另外一块Survivor区域(即to区域)中,然后清理所使用的的Eden以及Survivor区域(即from区域),并且将这些对象的年龄设置为1,以后对象在Survivor区每熬过一次Minor GC,就将对象的年龄+1,当对象的年龄达到某个值时(默认为15岁,可以手动设置),这些对象将会成为老年代。
    -XX:MaxTenuringThreshold —>设置对象在新生代中存活的次数

HotSpot JVM把年轻代分为了三部分:伊甸(Eden)区、幸存0(From)区、幸存1(To)区。默认比例为8:1:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一个Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到老年代中。因为年轻代中的对象基本都是朝生夕死(90%),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片,其原因是: 假设只有一个s0区,eden区回收之后,一部分对象存放到了s0区,此时eden区空间全部释放,内存都是连续的。但是因为s0区也会进行垃圾回收,它有一部分存活的对象进入到了Old区,还有一部分对象存活留下来,这时候s0区就产生了内存碎片,为了使s0区的内存空间相对连续,再分配一个s1区,大小和s0一样,每次垃圾回收的时候,将eden区和s0区存活的对象移动到s1区,这样永远都能保证s0或者s1的内存空间是连续的 。
image.png在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代,没有达到阈值的对象会被复制到To区域。经过这次GC后,Eden区和From区已经被清空,这时From和To会交换他们的角色,也就是新的To就是上次GC前的From,新的From就是上次GC前的To。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到To区被填满,To区被填满之后,会将所有对象移动到年老代。
image.png因为Eden区对象一般存活率较低,一般使用两块10%的内存作为空闲和活动区间,而另外80%的内存,则是用来给新建对象分配内存的。一旦发生GC,将10%的From活动区间和另外80%中存活的eden对象转移到10%的to空闲区间,接下来,将之前90%的内存全部释放,以此类推。

复制算法的缺点也是相当明显的

  • 它浪费了10%的内存,这很致命。
  • 如果对象的存活率很高,可以极端假设是100%存活,那么我们需要将所有对象都复制一遍,并将所有引用地址重置一遍。复制这一工作所花费的时间,在对象存活率达到一定程度时,将会变得不可忽视。所以从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须克服50%内存的浪费。

    标记清除

    老年代一般是由标记清除或者标记清除与标记整理的混合实现。算法分成标记和清除两个阶段,先标记出要回收的对象,然后统一回收这些对象。形如 :
    image.png
    用通俗的话解释一下标记清除算法,就是当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行。

劣势

  • 首先,效率低(递归与全堆对象遍历),而且在进行GC的时候,需要停止应用程序,这会导致用户体验非常差劲。
  • 其次,主要缺点则是这种方式清理出来的空闲内存是不连续的,这点不难理解,我们的死亡对象都是随机的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。而为了应付这一点,JVM就不得不维持一个内存的空闲列表,这又是一种开销。而且在分配数组对象的时候,寻找连续的内存空间会不太好找。

    标记压缩

    该算法全称:标记清除压缩算法,简称:标记整理算法
    image.png
    在整理压缩阶段,不再对标记的对象做回收,而是通过所有存活对象都像一端移动,然后直接清除边界以外的内存。可以看到,标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。

    GC采用的算法

    当前商业虚拟机都是采用分代收集算法,它根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,然后根据各个年代的特点采用最适当的垃圾收集算法。在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,就选用复制算法,而老年代因为对象存活率高,没有额外空间对它进行分配担保,就必须使用标记清除或者标记压缩算法来进行回收。
    image.png各算法比较:

  • 内存效率:复制算法>标记清除算法>标记算法(此处的效率只是简单的对比时间复杂度,实际情况不一定)

  • 内存整齐度:复制算法=标记整理算法>标记清除算法
  • 内存利用率:标记整理算法=标记清除算法>复制算法

可以看出,效率上来说,复制算法是当之无愧的老大,但是却浪费了太多内存,而为了尽量兼顾上面多提到的三个指标,标记/整理算法相对来说更平滑一些,但效率上依然不尽如人意,它比复制算法多了一个标记的阶段,又比标记/清除多了一个整理内存的过程


[

](https://blog.csdn.net/siyuanwai/article/details/119351274)
[

](https://blog.csdn.net/siyuanwai/article/details/119351274)