GC是什么?为什么我们要去使用它

本文探讨了垃圾回收(GC)在计算机科学中的重要性,解释了GC如何解决早期编程语言中静态分配策略的局限性。文章介绍了GC的起源、发展及在JVM中的应用,分析了对象识别和回收算法,以及不同引用类型对垃圾收集的影响。

GC(Garbage Collection)是各大语言的宠儿,也是计算机科学领域里很热门的一个话题。最早在JVM中有看过这个算法,后来发现即使是js这种脚本语言也是有GC的。单纯就JVM来说的话,GC算法也在不断地改进,成熟。从最早的串行到高顿吞吐量的并行,为了解决高延迟又演化出了CMS(Concurrent Mark Sweep),为了解决碎片的问题,又开发了G1.

为什么我们需要进行GC呢

在早期的编程语言中,程序运行中没有栈帧(Stack Frame)去维护,所以采用的是静态分配策略,这比动态分配要快不少,但是它有一个很不人性化的缺点就是需要在编译期的时候确定程序所需要的数据结构大小。

在1958年,Algol-58 语言首次提出了块结构(block-structured),块结构语言通过在内存中申请栈帧来实现按需分配的动态策略。在过程被调用时,帧(frame)会被压到栈的最上面,调用结束时弹出。栈分配策略赋予程序员极大的自由度,局部变量在不同的调用过程中具有不同的值,这为递归提供了基础。但是后进先出(Last-In-First-Out, LIFO)的栈限制了栈帧的生命周期不能超过其调用者,而且由于每个栈帧是固定大小,所以一个过程的返回值也必须在编译期确定。所以诞生了新的内存管理策略——堆(heap)管理。

现在计算机VS图灵机

现代计算机相对于图灵机来说,本质上的区别就在于资源有限性,所以在使用完各种资源以后,需要将其释放(release)。

那释放资源全都用GC可以嘛

GC本来就是给马大哈的程序员准备的,人为的手动释放资源很容易出问题,那我能不能在每次需要释放资源的时候都调用GC算法呢?这样不是一劳永逸嘛。

并不能,GC同样有它适用的和不适用的场景,在(socket/file handle)的使用中没有使用GC,很大一部分原因在于它的不确定性。你即使调用了GC你也不知道它啥时会回收,它到底会不会回收,这样的GC还是不可靠(雾,GC都是大猪蹄子

但是如果我们显示对资源进行回收就不一样了,调用了close/destroy后,资源百分百就被释放掉了

为啥在内存里面就可以用GC呢,有两个原因,第一是它具有独占性(当然你也可以叫唯一性)OS给每个运行的程序分配的内存都是唯一的,也是相互独立的,咋俩互不干扰,我晚一点回收对你也没有什么影响。第二点是内存够用,日常使用程序中也没见内存占用太高,我晚一点回收也不会OutOfMenmory,那我就放心大胆地用GC的回收机制。

由这两点可见,对于资源比较紧张的一些嵌入式的设备,还是手动释放资源来的较好。(不包括树莓派)

GC是什么

GC的本质是内存的自动管理,用来回收堆(Heap)中不再需要(使用)的对象。

我们知道内存其实也会被划分为各个区域的,常见的stack和heap。stack里面装的是局部变量,函数的调用结束以后也就回收了,这个是个固定的流程,所以不需要GC。Heap中的空间,用来在多个函数之间去共享数据,由程序自己来动态申请,这个时候我们就需要利用到GC了

GC由两大核心组成

  • 对象识别,其实也就是常说的判断对象是否存活,live object和garbage
  • 回收算法,什么时候回收,如何回收
那咋办嘛

先从对象识别开始说下吧,判断对象是live object还是garbage

在堆(Heap)里存放着Java中基本上所有对象的实例,当一个对象没有引用且不会再引用的时候就可以认为他已经死去,视为garbage

  1. 引用计数(Python和PHP都采用这种方式)

给对象一个引用计数器(在对象头加上一个counter),当引用的时候计数器就加一,引用失效就减一,引用次数为0的时候,该对象就失效了。

这个很像OS里面的PV原语,实现是很简单的,效率也比较高。但缺点也是显而易见的,就是很难去解决对象之间循环使用的问题。

举个栗子 如果现在有两个对象 OA OB ,OA.instance = OB;OB.instance=OA;它们两个相互引用,导致counter都不为0,于是都不会被回收

  1. 可达性分析(Tracing)

这个是现代语言使用最广泛的一种方式,从root对象开始,不断的去追踪(tracing),找到所有可以被引用的对象,那这些对象就可以被称作是可达的(reachable),剩下的对象自然就是不可达对象,视作garbage将其回收。

那么,你说的这个root对象,它是什么样子的呢?
  • Java 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中常量引用的对象
  • 方法区中类静态属性引用的对象

被堆中对象所引用的对象,它们就不算Roots了,这样就不会引起循环引用的问题。

引用

判定对象是否存活与“引用”有关。在 JDK 1.2 以前,Java 中的引用定义很传统,一个对象只有被引用或者没有被引用两种状态,我们希望能描述这一类对象:当内存空间还足够时,则保留在内存中;如果内存空间在进行垃圾手收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为了以下四种。不同的引用类型,主要体现的是对象不同的可达性状态reachable和垃圾收集的影响。

强引用(Strong Reference)

类似 "Object obj = new Object()" 这类的引用,就是强引用,只要强引用存在,垃圾收集器永远不会回收被引用的对象。但是,如果我们错误地保持了强引用,比如:赋值给了 static 变量,那么对象在很长一段时间内不会被回收,会产生内存泄漏。

软引用(Soft Reference)

软引用是一种相对强引用弱化一些的引用,可以让对象豁免一些垃圾收集,只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象。JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

弱引用(Weak Reference)

弱引用的强度比软引用更弱一些。当 JVM 进行垃圾回收时,无论内存是否充足,都会回收只被弱引用关联的对象。

虚引用(Phantom Reference)

虚引用也称幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响。它仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制,比如,通常用来做所谓的 Post-Mortem 清理机制。

事实上,软引用,弱引用和虚引用都可以被叫做弱引用

以弱引用的方式指向一个对象,弱引用不会保护该对象被 GC 回收。如果该对象被回收了,那么这个弱引用会被赋予一个安全值(一般为NULL)。

可以看一下这个wiki,讲了一下弱引用如何解决循环引用Dealing with reference cycles

参考

转载于:https://www.cnblogs.com/QuixoteY/p/11303247.html

### GC的含义及其在计算机科学中的作用 GC是“Garbage Collection”的缩写,意为垃圾回收。在计算机科学中,它主要用于自动管理内存,减少程序员手动释放不再使用的内存所带来的负担,同时避免因内存泄漏或重复释放内存而导致的程序错误[^2]。 GC在不同领域中有不同的含义。例如,在化学分析中,GC代表“气相色谱”;在通信领域,GC可以表示“通用类别目录”;而在编程语言中,尤其是JavaJavaScript等语言中,GC特指垃圾回收机制[^1]。 ### GC的作用 GC的主要作用是自动识别并回收程序中不再使用的内存,从而防止内存泄漏,提高程序的稳定性和开发效率。具体来说,GC通过以下方式实现内存管理: 1. **对象生命周期管理**:GC会跟踪程序中创建的对象,并在对象不再被引用时将其标记为可回收。 2. **内存释放**:一旦对象被标记为可回收,GC会在适当的时机释放其占用的内存资源。 3. **防止内存泄漏**:通过自动回收无用对象,GC有效避免了程序员忘记释放内存而导致的内存泄漏问题。 ### 为什么需要GC GC的存在对于现代编程语言和运行环境至关重要,原因如下: - **简化内存管理**:在没有GC的环境中,程序员需要手动分配和释放内存,这不仅增加了开发难度,也容易引入错误。GC通过自动管理内存,减轻了程序员的工作负担[^2]。 - **提升程序稳定性**:手动内存管理可能导致“悬空指针”、“重复释放”等问题,而GC可以避免这些常见的内存管理错误,从而提升程序的稳定性[^2]。 - **优化性能**:尽管GC在运行时会带来一定的性能开销,但现代GC算法(如G1、CMS等)已经能够在保证低延迟的同时高效回收内存[^2]。 然而,GC并非万能。在某些对资源释放时间敏感的场景(如文件句柄、网络连接等)中,GC的不确定性可能导致资源未能及时释放,因此在这些场景下通常需要程序员手动管理资源[^3]。 ### GC的调用方式 尽管GC可以自动运行,但在某些情况下,程序员可以通过编程方式建议JVM运行GC。例如,在Java中,可以通过`System.gc()`或`Runtime.getRuntime().gc()`方法通知垃圾回收器执行回收操作。然而,JVM并不保证GC会立即执行,且频繁调用该方法可能影响程序性能[^4]。 以下是一个简单的Java代码示例,展示了如何调用GC并查看内存相关信息: ```java public class GCTest { public static void main(String[] args) { Person p = new Person(); p = null; System.gc(); Runtime runtime = Runtime.getRuntime(); System.out.println("返回 Java 虚拟机试图使用的最大内存量。" + runtime.maxMemory()); System.out.println("返回 Java 虚拟机中的内存总量" + runtime.totalMemory()); System.out.println("返回 Java 虚拟机中的空闲内存量" + runtime.freeMemory()); } } ``` 上述代码中,`System.gc()`用于建议JVM执行垃圾回收,而`Runtime`类用于获取当前JVM的内存使用情况[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值