垃圾回收是一门编程语言中必不可少的一部分,不论是手动释放内存的C和C++,还是自动回收垃圾的Java和C#等语言。对于Java这样的语言,一般的开发者不强求关心对象回收和内存释放,但是理解垃圾回收对开发工作还是大有裨益的。
在编程语言中,普遍存在着循环引用这样的问题,垃圾回收器是如何处理循环引用呢,常用的垃圾回收有引用计数和引用对象遍历两种实现,它们各自又是如何处理循环引用呢?本文讲以JVM中的GC为例逐一回答这些问题。
何为循环引用
如果有两个或者以上的对象,它们彼此引用,就会造成循环引用。如下面的例子
1 2 3 4 5 6 7 8 |
|
代码中,a对象引用了b对象,b对象也引用了a对象,这种情况下a对象和b对象就形成了循环引用。
引用计数GC处理
什么是引用计数
引用计数是一种垃圾回收的形式,每一个对象都会有一个计数来记录有多少指向它的引用。其引用计数会变换如下面的场景
- 当对象增加一个引用,比如赋值给变量,属性或者传入一个方法,引用计数执行加1运算。
- 当对象减少一个引用,比如变量离开作用域,属性被赋值为另一个对象引用,属性所在的对象被回收或者之前传入参数的方法返回,引用计数执行减1操作。
- 当引用计数变为0,代表该对象不被引用,可以标记成垃圾进行回收。
如何处理
实际上单纯的基于引用计数实现的计数器无法处理循环引用带来的问题。
CPython的垃圾回收就是采用引用计数,采用引用计数的主垃圾回收器会清理垃圾,对于那些因为循环引用无法清理的对象,CPython会不时启动一个辅助的基于引用遍历的垃圾回收器来清理它们。
引用遍历GC处理
什么是引用对象遍历
垃圾回收器从被称为GC Roots的点开始遍历遍历对象,凡是可以达到的点都会标记为存活,堆中不可到达的对象都会标记成垃圾,然后被清理掉。 GC Roots有哪些
- 类,由系统类加载器加载的类。这些类从不会被卸载,它们可以通过静态属性的方式持有对象的引用。注意,一般情况下由自定义的类加载器加载的类不能成为GC Roots
- 线程,存活的线程
- Java方法栈中的局部变量或者参数
- JNI方法栈中的局部变量或者参数
- JNI全局引用
- 用做同步监控的对象
- 被JVM持有的对象,这些对象由于特殊的目的不被GC回收。这些对象可能是系统的类加载器,一些重要的异常处理类,一些为处理异常预留的对象,以及一些正在执行类加载的自定义的类加载器。但是具体有哪些前面提到的对象依赖于具体的JVM实现。
如何处理
基于引用对象遍历的垃圾回收器可以处理循环引用,只要是涉及到的对象不能从GC Roots强引用可到达,垃圾回收器都会进行清理来释放内存。
总结
基于引用计数的垃圾回收器无法处理循环引用导致的内存泄露问题,但是其在主流的JVM中很少,几乎所有的JVM都是采用引用对象遍历的方法,垃圾回收器都会处理循环引用潜在的问题。
相关推荐
然而,这种方法存在一个明显的缺陷——无法处理循环引用的情况。例如,如果有两个对象相互引用对方,即使程序中没有其他地方引用这两个对象,它们的引用计数也不会为0,因此不会被标记为垃圾。 **2. 根搜索算法** ...
当对象的引用计数变为0时,表示没有变量再使用这个对象,垃圾回收器就会回收这块内存。但这种机制对循环引用(两个或更多对象互相引用)处理不足,这时就需要其他策略介入。 接下来是标记清除(Mark and Sweep)...
但是,该算法的缺点是无法检测出循环引用。 2. 标记-清除算法 垃圾回收机制的另一个算法是标记-清除算法。该算法的主要思想是,首先标记出所有的GC Root对象,然后将这些对象的引用关系跟踪下去,并将所有的对象...
3. **对象回收**:当对象不再被任何引用所指向时,即认为该对象已成为垃圾,可被垃圾回收器回收。 具体来说,Java中的垃圾回收过程涉及以下几个关键步骤: 1. **标记(Marking)**:首先,垃圾回收器会从GC Roots...
然而,这种方法无法处理循环引用的问题,即两个或更多对象相互引用,导致它们的计数始终不为0。 对象引用遍历是现代JVM普遍采用的方法,它从一组称为根的对象开始,比如全局变量、静态变量和线程栈中的对象,沿着...
一个全面的垃圾回收器可以提供更全面的内存管理,特别是在处理复杂数据结构和长生命周期对象时。 在实践中,C++的内存管理策略往往结合手动管理、智能指针和定制的垃圾回收器,以达到最佳的性能和健壮性。设计和...
在Java中,对象的生命周期是由垃圾回收器来管理的,它自动释放不再被程序引用的对象,从而避免了内存泄漏等问题。 #### 三、垃圾回收的意义 在传统语言如C++中,程序员需要手动管理对象的内存,这意味着对象所占的...
然而,Java没有采用这种算法,因为处理循环引用关系非常困难,这可能导致错误地保留某些对象。 可达性分析是Java采用的方法,它通过建立对象和引用关系的图,选择一组称为GC Roots的对象(如虚拟机栈、本地方法栈中...
- 使用弱引用:弱引用不会增加对象的引用计数,即使有弱引用指向的对象,当其所有强引用都被解除后,仍然会被垃圾回收器回收。 - 及时解除事件监听器:在不再需要事件监听时,应及时调用`removeEventListener`方法...
- 分代收集算法:这是现代JVM垃圾回收器普遍采用的一种方法,它结合了上述几种算法。新创建的对象通常放在称为“新生代”的内存区域,经过多次垃圾回收仍然存活的对象则会被移动到“老年代”。新生代采用复制算法,...
"浅析Python垃圾回收机制" Python垃圾回收机制是指在Python程序执行过程中,动态申请内存空间,并在不再需要使用这些内存空间时释放它们,以避免内存泄漏。Python中的垃圾回收机制主要以引用计数为主,标记清除和分...
这种方法快速但存在缺点,如处理循环引用时的困难。 2. tracing算法:从根集(如全局变量、栈上的引用)开始遍历,所有可达的对象都被标记为存活,不可达的对象则被视为垃圾。此方法能有效处理循环引用,但清除过程...
在Java中,当一个对象不再有引用指向它时,就会被认为是“垃圾”,垃圾回收器会在适当的时候自动释放这部分内存,避免内存泄漏。同时,垃圾回收还能帮助整理内存碎片,提高内存利用率。 垃圾回收的实现并不简单,...
然而,这无法处理循环引用的问题,即两个或更多变量相互引用,导致它们的`refcount`永远不会降为0。为了解决这个问题,PHP 5.3引入了Zend GC(Garbage Collector),这是一种基于标记-清除算法的垃圾回收机制。 在...
为了解决循环引用问题,Python引入了两种额外的垃圾回收算法:标记清除和分代回收。标记清除算法基于可达性分析,它会标记所有从根节点(如全局变量和调用栈中的对象)可达的对象,然后清除未被标记的、不可达的对象...
然而,这种方法无法处理循环引用的问题,即两个或更多变量相互引用,但没有其他外部引用,导致这些变量的引用计数永远不为0。 为了解决这个问题,从PHP 5.3开始引入了一种更为复杂的垃圾回收机制,称为"标记-清除...
只要强引用还存在,垃圾回收器就不会回收掉被引用的对象。 2. 软引用 软引用描述的是非必需但还有用的对象。当系统内存不足时,软引用关联的对象会被放入回收范围进行二次回收。如果回收后内存仍不足,才会抛出...