1、Java 为什么要实现自动内存管理 ?
简化开发过程:通过内存自动管理可以避免手动分配和释放内存的麻烦,减少了内存泄漏和内存错误的风险,让研发能更专注于业务逻辑,不必纠结于内存管理的细节。
提高开发效率:垃圾回收器(Garbage Collector)能够自动追踪不再使用的对象,并释放它们占用的内存。这消除了手动跟踪和释放对象的需要,减少了开发人员的工作量,提高了开发效率。
可移植性好:研发人员不需要关心不同平台的内存管理差异,这些细节都交由虚拟机和垃圾回收器进行处理,Java的内存管理机制使得Java程序在不同的平台上运行更加容易。
2、java 自动内存管理并不是一劳永逸
Java的自动内存管理机制(垃圾回收器和垃圾回收算法的设计),确实可以大大简化开发人员对内存管理的工作,同样也带来了一系列的问题
内存占用和性能问题:不合适的内存使用、配置,可能导致内存占用过高或性能下降。如,长生命周期对象、内存泄漏、过频繁的垃圾回收等,都会影响应用程序的性能和稳定性。
垃圾回收停顿:虽然垃圾回收器一直都在优化减少停顿时间,但并不能完全消除。实时性要求高的系统对停顿时间很敏感
所以我们需要搞懂JVM内存管理机制,才能针对不同的场景合理使用
3、垃圾回收的机制
a、垃圾回收发生在哪里 ?
JVM 内存模型中程序计数器、栈、本地方法栈这 3 个区域是线程私有的,与线程同生共死,不涉及回收,所以垃圾回收的就在剩下的堆和方法区中了,堆中主要回收是对象,方法区的回收则主要是废弃常量和无用的类
b、什么情况下对象可以被回收?
JVM认为一个对象不再被引用,就代表该可以被回收了,目前有两种算法可以判断该对象是否可以被回收。
引用计数算法:通过对象的引用计数器判断对象是否被引用。即每当对象被引用时,该对象的引用计数器就会 + 1; 当引用失效时,计数器再 -1。对象引用计数器值为 0 时,表示该对象不再被引用,可以被回收。引用计数算法的实现简单,判断效率也很高,但它存在对象之间循环引用的问题。
可达性分析算法: 在垃圾回收时,以 GC Roots 对象为根对象开始遍历对象图,确定哪些对象是可达的(即不会被回收),而哪些对象是不可达的(即可被回收)。目前 HotSpot 虚拟机采用的就是这种算法。
c、哪些是 GC Roots 对象 ?
虚拟机栈(栈帧中的本地变量表)中引用的对象:当前线程中方法调用链上的所有对象。
方法区中的类静态属性引用的对象:被类声明为静态变量的对象。
方法区中常量引用的对象:被常量池中的常量引用的对象。
本地方法栈中引用的对象:在Java代码中调用本地方法后,本地方法中引用的对象
GC Roots 本身是不可被回收的,它们的存在保证了从根节点出发的对象的可达性。垃圾回收器通过追踪GC Roots对象的引用链,可以确定哪些对象是可达的,而哪些对象是不可达的,从而进行垃圾回收操作。
4、垃圾回收的三种方式
a、标记-清除算法(Mark and Sweep)
把垃圾对象所占据的内存标记为空闲内存,并记录在一个空闲列表(free list)中。当需要新建对象时,内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。该回收方式的原理非常简单,但会带来俩个缺点
内存碎片化:由于 Java 虚 拟机的堆中对象必须是连续分布的,因此可能出现总空闲内存足够,但是无法分配的极端情
内存分配效率低:如果是一块连续的内存空间,那么我们可以通过指针加法 (pointer bumping)分配。但对于空闲列表,Java 虚拟机则需要逐个访问列表中的项,来查找能够满足新建对象的大小的空闲内存
b、标记-整理算法(Mark and Compact)
在标记阶段(Mark)也会标记所有可达对象。然后,在整理阶段(Compact),将存活的对象压缩(Compact)到堆的一端,以释放不连续的内存空间。这种做法能够解决内存碎片化的问题,但代价是压缩算法的性能开销。
c、复制(copy)
把内存区域分为两等分,分别用两个指针 from 和 to 来维 护,并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时,便把存活的对 象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。复制这种回收方式同样能够解决内存碎片化的问题,但是它的缺点也极其明显,即堆空间的使用效率极其低下。
d、分代回收算法(Generational)
分代算法基于对象的生命周期将堆内存分为不同的代,如新生代和老年代。新生代中的对象通常具有较短的生命周期,而老年代中的对象具有较长的生命周期。不同代使用不同的垃圾回收算法,以便更好地适应对象的特性和内存使用模式。
G1的三种GC模式
young GC:当所有 eden region 被消耗无法再申请时触发一次young GC,活对象被拷贝到survivor region 或者晋升到 old region
mixed GC:回收整个young region,同时回收一部分old region。老年代占用达到一定阈值的时候触发
full GC:对象内存分配过快,mixed GC来不及回收,导致老年代被填满,会触发一次full GC
JVM在应用垃圾回收器时往往会综合上述几种回收方式,综合它们优点的同时规避它们的缺点,达到比较优的内存管理方式