深入理解Java虚拟机之经典垃圾收集器
为什么需要垃圾收集器
Java虚拟机需要对内存中无用的对象进行清理,以便为新对象腾出空间,避免OutOfMemoryError异常。Java虚拟机中的垃圾收集器可以自动回收无用对象,减少程序员手动管理的工作量。
垃圾回收的基本过程
垃圾回收的基本过程分为两个步骤:标记和垃圾回收。
标记阶段:
- 遍历根对象,将根对象活跃的对象标记为存活。
- 遍历存活对象的所有引用,将其所包含的对象标记为存活。
- 重复上一步的过程,直到所有可到达的对象都标记为存活,这个过程被称为“可达性分析”。
垃圾回收阶段:
- 将重复利用的内存空间挑选出来,这部分内存空间将作为下一轮存储使用过的对象空间。
- 遍历整个堆空间,将未被标记的对象移回到空间池,将处理过的对象移动到下一轮可用的内存空间中。
常见的垃圾回收算法
标记-清除算法
标记-清除算法分为两个阶段:标记,清除。
- 标记阶段:所有的存活对象都被标记,未被标记的都是垃圾对象。
- 清除阶段:被判定为垃圾的对象将会被清除。
缺陷:
1. 标记阶段和清除阶段都需要暂停整个应用程序,会影响用户体验。
2. 清除后内存空间不连续而且散乱,会影响后续分配内存的效率。
3. 对象引用可以被多个对象引用,如果其中一个对象被回收,则其可能影响其他对象的使用,引发dangling pointer
问题。
复制算法
复制算法将可用的内存空间与垃圾回收保持完全分离,垃圾对象被回收后,剩下的内存会被整理,从而避免内存碎片化的问题。
步骤:
1. 将总可用内存空间一分为二,每个内含一半的空间。
2. 选取一半的空间进行对象分配。
3. 遍历已分配的对象,将其复制到另一半空间中。
4. 清理已分配的半部分空间,使其可变为下一轮分配使用。
复制算法的不足:
1. 只有一半的内存作为可用空间,不满足如今内存容量需求。
2. 复制算法只有一半的内存作为可用空间,正常情况下每一个对象被复制的次数都比较少,如果要存放所有的对象,那么我们需要将这个内存空间分成N份,复制N-1次才能将对象复制到这N份内存当中,复制的次数会明显增多。
标记-整理算法
去除标记-清除算法的缺陷,可以用标记-整理算法。标记-整理算法在标记阶段让不活跃的对象变成垃圾,清除阶段将所有存活的对象压缩到连续的内存空间中,并把未压缩的内存清理干净。
步骤:
1. 标记存活对象。
2. 将存活对象移到连续的内存地址中,而不是清除所有未被标记的存活对象。
3. 重置所有指针,以正确指向移动后的位置。
4. 清理不需要的内存区域。
示例说明
示例1:标记-清除算法的不足
在代码中定义了一个对象o1
,并将其引用赋值给了o2
。
Object o1 = new Object();
Object o2 = o1;
在执行过程中,o1
和o2
两个引用在栈内存上均指向堆内存中的同一对象。
若在外部执行了如下操作:
o1 = null;
此时,堆内存中的对象被标记为不再被引用,即为垃圾对象,但由于o2
仍持有该对象的引用,此对象并不会被标记-清除算法回收,这就导致了内存泄漏。
示例2:复制算法的缺陷
在以下一个数组中进行对象分配:
Object[] arr = new Object[1000000];
当使用复制算法将数组分割为两个可用的内存空间进行对象分配时,如果数组的大小比分割后的空间还要大,就无法进行对象分配。此时需要使用滚动式压缩算法,在回收时将存活对象从内存里面移动到一端,并在移动后更新所有的被指向该对象的指针,从而减少内存压力。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:深入理解Java虚拟机之经典垃圾收集器 - Python技术站