目录

  1. 从碎片整理看垃圾收集
  2. 垃圾收集从何而来?
  3. 为什么需要 GC
  4. 其他语言的垃圾收集方案
  5. Java 垃圾收集分析
  6. GC 关注的区域(哪些内存需要回收)
  7. 附录

首先从一个经常进行的操作(磁盘碎片整理)开始,随后再逐渐引入垃圾收集的概念

从碎片整理看垃圾收集

如果使用过机械硬盘,对于碎片整理应该都不陌生。机械硬盘使用一段时间后最好进行一次磁盘整理,避免磁盘中碎片过多造成存储空间的浪费。其实上述的这个过程与垃圾收集类似,都是为了能够更加高效的使用存储空间,进而对空间进行某些清扫操作

垃圾收集从何而来?

垃圾收集,不是 Java 语言的伴生产物,早在 1960 年,第一门开始使用内存动态分配和垃圾收集技术的 Lisp 语言诞生时就已经支持垃圾回收机制。如今,垃圾收集机制是 Java 的招牌能力,极大地提高了开发效率。即使经过如此长时间的发展,Java 的垃圾收集机制仍然在不断的演进中,不同大小的设备、不同特征的应用场景,对垃圾收集提出了新的挑战,这当然也是面试的热点

✨本文主要讲解垃圾收集的三个经典问题中的第一个问题:哪些内存需要回收

  1. 哪些内存需要回收?
  2. 什么时候回收垃圾?
  3. 如何回收垃圾?

🎶虽然经常将垃圾收集称为 GC,但是在不同的上下文中 GC 指代的含义是不同的,比如本文中的 GC 可能指代的是Garbage Collection,而如果在讨论垃圾收集算法时,GC 指代的可能时Garbage Collector,因此需要和特定的上下文进行关联,在后面的文章中不再过多解释

为什么需要 GC

🤔为什么需要垃圾回收,如果不进行垃圾回收会怎么样?

对于高级语言来说,一个基本认知是:如果不进行垃圾回收,内存迟早都会被消耗完,因为不断地分配内存空间而不进行回收,就好像不停地生产生活垃圾而从来不打扫一样,迟早有一天空间会被垃圾占满;就算在程序执行的过程中幸运的没有出现内存溢出的问题,也会由于对象存在于内存空间中,但不能继续被使用而造成内存泄露的问题。

📓垃圾收集除了能够堆垃圾的收集和释放之外,也可以清除内存里的记录碎片(类似磁盘碎片整理),将所占用的堆内存移到堆的一端,以便 JVM 将整理出的内存分配给新的对象

其他语言的垃圾收集方案

🤔 GC 拥有如此强大的功能,是否所有的语言都采用了 GC?

并不是所有的语言都采用了 GC。Lisp 并不是最初的语言,所以可以想象:早于 Lisp 的语言(C/C++)都是需要开发人员自主进行内存管理的。例如:如果使用 C/C++语言,垃圾回收基本上是手工进行的。开发人员使用new关键字进行内存申请,并使用delete关键字进行内存释放。

1
2
3
4
MibBridge *pBridge= new cmBaseGroupBridge();
//如果注册失败,使用Delete释放该对象所占内存区域
if (pBridge->Register(kDestroy) != NO ERROR)
delete pBridge;

📓这种方式可以灵活控制内存释放的时间,但是会给开发人员带来频繁申请和释放内存的管理负担。倘若有一处内存区间由于程序员编码的问题忘记被回收,那么就会产生内存泄漏,垃圾对象永远无法被清除,随着系统运行时间的不断增长,垃圾对象所耗内存可能持续上升,直到出现内存溢出并造成应用程序崩溃

Java 垃圾收集分析

🆚Java 垃圾收集优劣分析

👍Java 语言也支持垃圾回收机制,能够自动进行内存管理,使得无需开发人员手动参与内存的分配与回收,降低内存泄漏和内存溢出的风险,将业务开发人员从繁重的内存管理中释放出来,更专注于业务开发

😣但是由于自动内存管理由底层 JVM 操作,如果过度依赖于自动,会弱化 Java 开发人员在程序出现内存溢出时定位问题和解决问题的能力

📓了解 JVM 的自动内存分配内存回收原理就显得非常重要,只有在真正了解 JVM 是如何管理内存后,才能够在遇见outofMemoryError时,快速地根据错误异常日志定位问题和解决问题。

GC 关注的区域(哪些内存需要回收)

🤔什么是垃圾(哪些内存需要回收)?

编码人员的角度来看,垃圾是指在运行程序中没有任何指针指向的对象

JVM 调优人员的角度来看,垃圾是 JVM 中的堆和方法区

📓由于程序计数器、虚拟机栈、本地方法栈随线程而生,也随线程而灭;栈帧随着方法的开始而入栈,随着方法的结束而出栈。这几个区域的内存分配和回收都具有确定性,所以这几个区域不需要考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而对于 JVM 堆方法区,只有在程序运行期间才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的正是这部分内存

如果更进一步分析,垃圾收集器不仅可以对堆中的年轻代回收,也可以对老年代回收,甚至是全栈和方法区的回收。Java 堆是垃圾收集器的工作重点

垃圾收集小口诀:频繁收集年轻代(Young),较少收集老年代(Old),基本不动永久代(Perm/元空间)

附录

JVM 底层原理最全总结

oracle 官网关于垃圾回收的介绍