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

参考


GC是什么?为什么我们要去使用它的更多相关文章

  1. [译]GC专家系列1: 理解Java垃圾回收

    原文链接:http://www.cubrid.org/blog/dev-platform/understanding-java-garbage-collection/ 了解Java的垃圾回收(GC)原 ...

  2. Java GC专家系列1:理解Java垃圾回收

    了解Java的垃圾回收(GC)原理能给我们带来什么好处?对于软件工程师来说,满足技术好奇心可算是一个,但重要的是理解GC能帮忙我们更好的编写Java应用程序. 上面是我个人的主观的看法,但我相信熟练掌 ...

  3. JVM性能调优,GC

    刚刚做完了一个项目的性能测试,“有幸”也遇到了内存泄露的案例,所以在此和大家分享一下. 主要从以下几部分来说明,关于内存和内存泄露.溢出的概念,区分内存泄露和内存溢出:内存的区域划分,了解GC回收机制 ...

  4. JVM GC 机制与性能优化

    目录(?)[+] 1 背景介绍 与C/C++相比,JAVA并不要求我们去人为编写代码进行内存回收和垃圾清理.JAVA提供了垃圾回收器(garbage collector)来自动检测对象的作用域),可自 ...

  5. GC(Garbage Collection)垃圾回收机制

    1.在垃圾回收器中,程序员没有执行权,只有通知它的权利. 2.程序员可以通过System.gc().通知GC运行,但是Java规范并不能保证立刻运行. 3.finalize()方法,是java提供给程 ...

  6. GC回收算法

    GC回收算法 https://www.cnblogs.com/missOfAugust/p/9528166.html Java语言引入了垃圾回收机制,让C++语言中令人头疼的内存管理问题迎刃而解,使得 ...

  7. GC回收算法--当女友跟你提分手!

    Java语言引入了垃圾回收机制,让C++语言中令人头疼的内存管理问题迎刃而解,使得我们Java狗每天开开心心地创建对象而不用管对象死活,这些都是Java的垃圾回收机制带来的好处.但是Java的垃圾回收 ...

  8. Lua GC机制

    说明 分析lua使用的gc算法,如何做到分步gc,以及测试结论 gc算法分析 lua gc采用的是标记-清除算法,即一次gc分两步: 从根节点开始遍历gc对象,如果可达,则标记 遍历所有的gc对象,清 ...

  9. 浅谈 JVM GC 的安全点与安全区域

    OopMap 前文我们说到,JVM 采用的可达性分析法有个缺点,就是从 GC Roots 找引用链耗时. 都说他耗时,他究竟耗时在哪里? GC 进行扫描时,需要查看每个位置存储的是不是引用类型,如果是 ...

  10. .NET CoreCLR开发人员指南(上)

    1.为什么每一个CLR开发人员都需要读这篇文章 和所有的其他的大型代码库相比,CLR代码库有很多而且比较成熟的代码调试工具去检测BUG.对于程序员来说,理解这些规则和习惯写法非常的重要. 这篇文章让所 ...

随机推荐

  1. Asp.net HttpClient Proxy(Fiddler)

    <system.net> <defaultProxy> <proxy bypassonlocal="False" usesystemdefault=& ...

  2. GRPC Oauth Identity

    gRPC中集成asp.net identity实现oAuth认证 在asp.net core 3.0中开启identity认证 asp.net core 3.0种需要导入的identity包与core ...

  3. storm资源冲突

    本方案出自<storm应用实践:实时事务处理之策略>,简略的写了一部分,详细的方法就看书吧 1)集群中工作进程冲突: 解决:通过查看stormUI cluster summary中的fre ...

  4. python连接 elasticsearch 查询数据,支持分页

    使用python连接es并执行最基本的查询 from elasticsearch import Elasticsearch es = Elasticsearch(["localhost:92 ...

  5. SQL系统优化

    1 系统优化介绍 在我们的项目中,由于客户的使用时间较长或客户的数据量大,造成系统运行速度慢,系统性能下降就容易造成数据库阻塞.这是个非常痛苦的事情,用户的查询.新增.修改等需要花很多时间,甚至造成系 ...

  6. 我在生产项目里是如何使用Redis发布订阅的?(二)Java版代码实现(含源码)

    上篇文章讲了在实际项目里的哪些业务场景用到Redis发布订阅,这篇文章就讲一下,在Java中如何实现的. 图解代码结构 发布订阅的理论以及使用场景大家都已经有了大致了解了,但是怎么用代码实现发布订阅呢 ...

  7. 【风哥干货】快速解决Oracle数据库故障必备的20个脚本与命令

    1.操作系统性能(通常故障出现时最先检查的内容)top.topas.vmstat.iostat.free.nmon 2.万能重启方法 如应急情况,需要重启数据库:tail -100f <对应路径 ...

  8. 走近Java之幕后的String

    前几天,有个同事问了我一个表面看起来显而易见的问题,是关于String的,我们一起来看一下(如果有说的不正确的地方,欢迎大家指正). java中,字面量在编译期计算,并且String字面量作为常量,存 ...

  9. Apache Dubbo已不再局限于Java语言

    2017 年 9 月 7 日,在沉寂了4年之后,Dubbo 悄悄的在 GitHub 发布了 2.5.4 版本.随后又迅速发布了 2.5.5.2.5.6.2.5.7 等release.在 2017年 1 ...

  10. centos 上安装redis 3.0.5

    官网下载安装包,直接使用make编译,报如下错误 : [root@localhost redis-3.0.5]# make cd src && make all make[1]: 进入 ...