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. CentOS7系统安装

    CenOS7安装系统 镜像下载地址: http://isoredirect.centos.org/centos/7/isos/x86_64/ https://mirrors.aliyun.com/ce ...

  2. C++学习笔记 之 运算符

    用来执行特定的数字或逻辑操作,C++主要提供的运算符如下: 算数运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 算数运算符: 主要运算符:+(加).-(减) .*(乘) ./(除) ...

  3. 跟我学SpringCloud | 第八篇:Spring Cloud Bus 消息总线

    SpringCloud系列教程 | 第八篇:Spring Cloud Bus 消息总线 Springboot: 2.1.6.RELEASE SpringCloud: Greenwich.SR1 如无特 ...

  4. IO侦探:多进程写ceph-fuse单文件性能瓶颈侦查

    近期接到ceph用户报案,说是多进程direct写ceph-fuse的单个文件,性能很低,几乎与单进程direct写文件的性能一样.关乎民生,刻不容缓,笔者立即展开侦查工作~ 一.复现案情,寻踪追记 ...

  5. 在windowx的Hyper-v 安装CentOS系统

    博客写的很少,一方面是因为我觉得目前很多博客都是相互抄袭,或者有很多部分都是重复的内容.而我自己再去写同样的内容的画,有点浪费时间. 所以,如果我要写,我希望是写一些与众不同,或者重复率比较低的内容, ...

  6. Ural 2072:Kirill the Gardener 3(DP)

    http://acm.timus.ru/problem.aspx?space=1&num=2072 题意:有n朵花,每朵花有一个饥渴值.现在浇花,优先浇饥渴值小的(即从小到大浇),浇花需要耗费 ...

  7. mysql计算日期之间相差的天数

    TO_DAYS(NOW()) - TO_DAYS(createTime) as dayFactor,

  8. C#实体是什么

    实体 一般是指三层结构的数据库访问形式中,用于映射数据表或视图中一条记录的类的实例.例如现有数据表A 内涵 ID int(4) Info(nvarchar 50)两个字段,而有其对应的类 public ...

  9. leetcode 141 Linked List Cycle Hash fast and slow pointer

    Problem describe:https://leetcode.com/problems/linked-list-cycle/ Given a linked list, determine if ...

  10. Java项目案例之---计算公司员工的工资(面向对象复习)

    计算公司员工的工资(面向对象复习) 某公司的雇员分为以下若干类: Employee:这是所有员工总的父类,属性:员工的姓名,员工的生日月份.方法:double getSalary(int month) ...