写点什么

Java 垃圾回收机制小结以及优化建议,kafka 的工作原理图

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:2178 字

    阅读完需:约 7 分钟

Java 的内存是不用我们开发者自己来管理的,这个大家都知道,但是那它到底是怎么运作的呢? 我们都知道 GC,也就是垃圾回收机制,但到底什么是 GC。 我们一起来看看。


什么是 GC




垃圾回收是一种自动的存储管理机制。当一些被占用的内存不再需要时,就应该予以释放,以让出空间,这种存储资源管理,称为垃圾回收(garbage collection)。垃圾回收器可以让程序员减轻许多负担,也减少程序员犯错的机会。 我们来主要看看 Java 的 gc 机制。


整个 Java 堆可以切割成为三个部分:


  1. Young(年轻代):

  2. Eden(伊利园):存放新生对象。

  3. Survivor(幸存者):存放经过垃圾回收没有被清除的对象。

  4. Tenured(老年代):对象多次回收没有被清除,则移到该区块。

  5. Perm:存放加载的类别还有方法对象。


GC 会造成什么影响




在开始学习 GC 之前你应该知道一个词:stop-the-world。不管选择哪种 GC 算法,stop-the-world 都是不可避免的。 也就是说,当垃圾回收开始清理资源时,其余的所有线程都会被停止。所以,我们要做的就是尽可能的让它执行的时间变短。如果清理的时间过长,在我们的应用程序中就能感觉到明显的卡顿。


什么情况下 GC 会执行




因为它对系统影响很明显,所以它到底在什么时候执行呢?


总的来说,有两个条件会触发主 GC:


  1. 当应用程序空闲时,即没有应用线程在运行时,GC 会被调用。因为 GC 在优先级最低的线程中进行,所以当应用忙时,GC 线程就不会被调用,但以下条件除外。

  2. Java 堆内存不足时,GC 会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM 就会强制地调用 GC 线程,以便回收内存用于新的分配。若 GC 一次之后仍不能满足内存分配的要求,JVM 会再进行两次 GC 作进一步的尝试,若仍无法满足要求,则 JVM 将报“out of memory”的错误,Java 应用将停止。


由于是否进行主 GC 由 JVM 根据系统环境决定,而系统环境在不断的变化当中,所以主 GC 的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主 GC 是反复进行的。


垃圾回收的一般步骤




之前已经了解到 Java 堆被主要分成三个部分,而垃圾回收主要是在 Young(年轻代)和 Tenured(老年代)工作。 而 年轻代 又包括 Eden(伊利园)和两个 Survivor(幸存者)。 下面我们就来看看这些空间是如何进行交互的:


1、首先,所有新生成的对象都是放在年轻代的 Eden 分区的,初始状态下两个 Survivor 分区都是空的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。



2、当 Eden 区满的的时候,小垃圾收集就会被触发。



《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


3、当 Eden 分区进行清理的时候,会把引用对象移动到第一个 Survivor 分区,无引用的对象删除。



4、在下一个小垃圾收集的时候,在 Eden 分区中会发生同样的事情:无引用的对象被删除,引用对象被移动到另外一个 Survivor 分区(S1)。此外,从上次小垃圾收集过程中第一个 Survivor 分区(S0)移动过来的对象年龄增加,然后被移动到 S1。当所有的幸存对象移动到 S1 以后,S0 和 Eden 区都会被清理。注意到,此时的 Survivor 分区存储有不同年龄的对象。



5、在下一个小垃圾收集,同样的过程反复进行。然而,此时 Survivor 分区的角色发生了互换,引用对象被移动到 S0,幸存对象年龄增大。Eden 和 S1 被清理。



6、这幅图展示了从年轻代到老年代的提升。当进行一个小垃圾收集之后,如果此时年老对象此时到达了某一个个年龄阈值(例子中使用的是 8),JVM 会把他们从年轻代提升到老年代。



7、随着小垃圾收集的持续进行,对象将会被持续提升到老年代。



8、这样几乎涵盖了年轻一代的整个过程。最终,在老年代将会进行大垃圾收集,这种收集方式会清理-压缩老年代空间。



也就是说,刚开始会先在新生代内部反复的清理,顽强不死的移到老生代清理,最后都清不出空间,就爆炸了。


与堆配置相关的参数


| 参数 | 描述 |


| --- | --- |


| -Xms | JVM 启动的时候设置初始堆的大小 |


| -Xmx | 设置最大堆的大小 |


| -Xmn | 设置年轻代的大小 |


| -XX:PermSize | 设置持久代的初始的大小 |


| -XX:MaxPermSize | 设置持久代的最大值 |


优化建议:


根据 GC 的工作原理,我们可以通过一些技巧和方式,让 GC 运行更加有效率


  1. 最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为 null。好的做法是:如果程序允许,尽早将不用的引用对象赋为 null,这样可以加速 GC 的工作;

  2. 尽量少用 finalize 函数。finalize 函数是 Java 提供给程序员一个释放对象或资源的机会。但是,它会加大 GC 的工作量,因此尽量少采用 finalize 方式回收资源;

  3. 如果需要使用经常使用的图片,可以使用 SoftReference 类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起 OutOfMemory;

  4. 注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对 GC 来说,回收更为复杂,所以使用结束应立即置为 null,不要等堆在一起。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费;

  5. 当程序有一定的等待时间(注意,是有一定等待时间时),程序员可以手动执行 System.gc(),通知 GC 运行,但是 Java 语言规范并不保证 GC 一定会执行。使用增量式 GC 可以缩短 Java 程序的暂停时间。System.gc(); Runtime.getRuntime().gc() 这个方法对资源消耗较大尽量不要手动去调用这个方法,不然可能引起程序的明显卡顿

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Java垃圾回收机制小结以及优化建议,kafka的工作原理图