深入理解JAVA虚拟机3:认识GC

一、什么是GC

Garbage Collection 垃圾收集

二、为什么要进行GC

不进行垃圾收集的话,内存中的垃圾会越来越多,最终内存满了,机器就会坏掉,坏掉就要买过,买过就要花钱

三、回收什么内容

要回收的当然是内存中的垃圾了,那么什么内存中的垃圾又指的是什么呢?

垃圾就是死去的对象,那么问题又来了,怎么判断对象有没有死呢?这里就要介绍两种方法了

  • 引用计数法

给对中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1,当合时刻计数器为0的对象就是不可能再被使用的。是不是感觉很简单?简单是简单,但是这个方法有一个致命的缺点,就是它很难解决对象直接的循环引用的问题。所以JVM并没有采用该算法。

  • 可达性分析法

通过一系列"GC Roots"的对象作为起始点,从这些节点向下搜索,搜索走过的路径,凡是与根节点不可达的对象,就被认为是不可以的对象,即可回收的对象。这样的话,即使存再循环引用,但如果循环引用的对象与根节点不可达,则也会被判断我可回收对象,这样就完美解决了引用计数的缺点。

  • 那么,哪些对象可以作为GC Roots呢?
  1. 虚拟机栈中的引用的对象。
  2. 方法区中的类静态属性引用的对象。
  3. 方法区中的常量引用的对象。
  4. 本地方法栈中JNI的引用的对象。
  • 再谈引用
  1. 强引用 : 只有在引用对象root不可达的情况下才会标识为可回收,垃圾回收才可能进行回收
  2. 软引用 : 无论其引用的对象是否root可达,在响应内存需要时,由垃圾回收判断是否需要回收。
  3. 弱引用 :用来描述非必需对象。即使在root算法中 其引用的对象root可达到,只能生存到下一次垃圾回收之前。
  4. 虚引用:无法通过虚引用获得一个对象的实例,设置虚引用的目的就是能在这个对象被收集器回收时收到一个系统通知
  • 当在可达性分析中不可达的对象真的死了吗?

显然不是的,不然我就不会问这个问题了...当对象所有引用被释放后,对象进入可复活状态

当:对象被判断有必要执行finalize()方法时(对象覆盖了finalize()方法且finalize()方法没有被虚拟机调用过),它是仍有可能复活的,但是它只有一次机会,也就是说复活之后再次死了的话,那就是真的死了...因为任何一个对象的finalize()方法都只会被系统自动调用一次。

四、如何进行GC (GC算法)

标记-清除算法 Mark-Sweep

这是一个非常基本的GC算法,它是现代GC算法的思想基础,分为标记和清除两个阶段:先把所有活动的对象标记出来,然后把没有被标记的对象统一清除掉。但是它有两个问题,一是效率问题,两个过程的效率都不高。二是空间问题,清除之后会产生大量不连续的内存。

复制算法 Copying

复制算法是将原有的内存空间分成两块,每次只使用其中的一块。在GC时,将正在使用的内存块中的存活对象复制到未使用的那一块中,然后清除正在使用的内存块中的所有对象,并交换两块内存的角色,完成一次垃圾回收。它比标记-清除算法要高效,但不适用于存活对象较多的内存,因为复制的时候会有较多的时间消耗。它的致命缺点是会有一半的内存浪费。

标记整理算法 Mark-Compact

标记整理算法适用于存活对象较多的场合,它的标记阶段和标记-清除算法中的一样。整理阶段是将所有存活的对象压缩到内存的一端,之后清理边界外所有的空间。它的效率也不高。

三种算法的异同

共同点

  • 三个算法都基于根搜索算法去判断一个对象是否应该被回收,而支撑根搜索算法可以正常工作的理论依据,就是语法中变量作用域的相关内容。因此,要想防止内存泄露,最根本的办法就是掌握好变量作用域,而不应该使用前面内存管理杂谈一章中所提到的C/C++式内存管理方式。
  • 在GC线程开启时,或者说GC过程开始时,它们都要暂停应用程序(stop the world)。

区别

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

五、总结

该篇主要介绍了GC,从是什么,为什么,怎么样这几个方面对GC进行了学习,下一篇主要讲常用的一些垃圾收集器。

猜你喜欢

转载自blog.csdn.net/qq_37410328/article/details/82711086
今日推荐