C/C++语言是非常底层的语言,没有内置的垃圾回收机制,是通过new关键字申请内存资源,通过delete关键字释放内存资源。。这意味着程序员需要手动分配和释放内存,并负责管理程序的堆栈内存和堆内存。
为了确保堆内存的正确释放,程序员必须跟踪所有分配的内存,并在使用完毕后手动释放它们。这是非常困难和容易出错的,因为程序员需要考虑到对象的生命周期和所有可能的退出路径。
如果,程序员在某些位置没有写delete进行释放,那么申请的对象将一直占用内存资源,最终可能会导致内存溢出。
Java语言的垃圾回收是指Java虚拟机自动管理内存分配和释放的机制。Java虚拟机会周期性地扫描内存中的对象,找到不再被引用的对象并回收它们的内存,避免了手工释放内存的复杂性和风险。
Java垃圾回收的实现采用的是可达性算法。当一个对象没有任何引用指向它时,即成为无用对象。Java虚拟机会通过一连串的引用关系,判断一个对象是否还能被其他对象访问,如果不能,就会将其回收。
Java垃圾回收的优点是可以让程序员专注于业务逻辑,而不需要关心内存的分配与释放。缺点是可能会影响程序的性能,因为垃圾回收需要消耗一定的计算资源。同时,由于Java虚拟机并不知道如何处理本地资源,因此需要手动释放本地资源,避免发生内存泄漏。
常见的垃圾回收算法有:引用计数法、标记清除法、标记压缩法、复制算法、分代算法等。
引用计数法是一种垃圾回收算法,其核心思想是给每个对象添加一个计数器,用于记录当前有多少个引用指向该对象,当引用计数为0时,可以将该对象视为垃圾进行回收。
引用计数法是最早的垃圾回收算法之一,它的实现非常简单,每个对象都有一个引用计数器,当对象被引用时计数器加一,当引用被取消时计数器减一,当计数器为0时,该对象就可以被回收了。引用计数法的优点是实现简单,回收及时,但是它存在循环引用的问题。
循环引用是指两个或多个对象互相引用形成一个闭环的情况,这种情况下,引用计数器的引用计数永远不会为0,导致这些对象无法被垃圾回收器回收,造成内存泄漏的问题。
优点:
缺点:
案例:
class TestA{
public TestB b;
}
class TestB{
public TestA a;
}
public class Main{
public static void main(String[] args){
A a = new A();
B b = new B();
a.b=b;
b.a=a;
a = null;
b = null;
}
}
虽然a和b都为null,但是由于a和b存在循环引用,这样a和b永远都不会被回收。
标记清除法是一种垃圾回收算法,其基本原理是在程序执行时标记所有可以访问的对象,然后清除所有没有被标记的对象,将空间进行回收。
该算法的具体步骤如下:
该算法的优点是能够回收所有可以访问的未使用的内存空间,而不会回收正在使用的内存空间。缺点是在执行过程中会暂停程序,因为需要在执行过程中遍历所有可访问的对象。此外,该算法可能会导致内存碎片的问题,因为清除的对象可能不是连续的内存空间。
流程图解:
没有被标记的对象将会回收清除掉,而被标记的对象将会留下,并且会将标记位重新归0。接下来就不用说了,唤醒停止的程序线程,让程序继续运行即可。
可以看到,标记清除算法解决了引用计数算法中的循环引用的问题,没有从root节点引用的对象都会被回收。
同样,标记清除算法也是有缺点的:
垃圾回收的标记压缩算法是一种内存回收机制,它主要是为了解决动态内存分配造成的内存碎片问题,以及避免内存泄漏等问题。下面介绍其主要原理:
优缺点同标记清除算法,解决了标记清除算法的碎片化的问题,同时,标记压缩算法多了一步,对象移动内存位置的步骤,其效率也有有一定的影响。
复制算法是一种基于空间换时间的垃圾回收算法。其主要思想是将堆空间分为两个部分:From空间和To空间。在程序运行过程中,所有的新生代对象都被分配在From空间中,当From空间被占用到一定程度时,就会触发垃圾回收机制。
回收的过程如下:
通过复制算法,可以有效避免内存碎片的产生,提高垃圾回收效率。但是它需要两倍的堆空间,并且需要对整个堆空间进行复制操作,因此对于大型对象或存活率较高的情况,复制算法的效率会下降,不适合用来回收老年代对象。
优点:
缺点:
分代算法是目前主流的垃圾回收算法之一。
分代算法的基本思想是将内存中的对象按照其存活时间分为不同的“代”,然后在垃圾回收时优先考虑清理那些存活时间短的对象,从而减少垃圾回收的时间和工作量。
一般来说,分代算法将内存分为三代:年轻代、中年代和老年代。年轻代是指所有新创建的对象的集合,这些对象通常有较短的生命周期,很快就会被垃圾回收。中年代是指存活时间较长的对象,通常是在年轻代经历了多次垃圾回收后仍然存活的对象,这些对象不太容易被回收。而老年代则是指存活时间最长的对象,这些对象通常是一些单例对象或缓存对象,它们的存活时间非常长,很少被回收。
分代算法中的年轻代通常采用“复制”算法进行垃圾回收。具体来说,年轻代被分为两个等大小的空间,称为“from”空间和“to”空间。在垃圾回收时,所有存活的对象都会被复制到to空间中,而from空间则被清空。年轻代空间的大小一般比较小,所以它的垃圾回收比较频繁,但是回收的效率也比较高。
中年代和老年代通常采用“标记-清除”或“标记-整理”算法进行垃圾回收。在标记-清除算法中,首先标记所有存活的对象,然后清除所有未标记的对象。在标记-整理算法中,标记所有存活的对象,然后将这些对象紧凑排列,清理掉其他的空间。
分代算法其实就是这样的,根据回收对象的特点进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法。
亲爱的读者,
我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。
如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。
我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。