带着问题去思考!大家好

相对.NET 来说。CLR去处理了,C,C++这些就需要手动去垃圾回收。

  GC大部分容易察觉的性能问题。其实很多问题实际是哪个都是由于对垃圾回收器的行为和预期结果理解有误。在,NET环境中,你需要更多的关注内存的性能,那么接下里我们主要是讲内存性能问题。

  GC实际上会调整体提高内存堆[1]的性能,因为他能高效的完成内存分配和碎片整理工作。

  在Windows的本机代码模式下,内存堆维护着一张空闲内存块的列表,用于内存的分配,尽量用低碎片化的内存堆,因为长时间运行,还要考虑内存碎片问题,内存占用率会持续增长。所以本机代码程序用大量代码实现了自己的内存分配机制,把默认的malloc函数给替换掉

  在.NET环境中,内存分配的工作量会很小,因为内存总是整段分配的。所以不会比内存的扩大,减小和比较增加多少开销,不存在遍历空闲内存列表,几乎不会出现内存碎片,GC内存堆的效率还会更高的,因为连续分配的多个对象往往在内存堆中也是连续存放,提高就近访问的可能性。

  在默认的内存分配流程中,有一小段代码会先检查目标对象的大小,看看内存分配缓冲区中所剩的内存够不够,如果内存分配缓冲区已耗尽,就会交由GC分配程序来检索足以容纳目标对象的空闲内存。然后一个新的分配缓冲区就会被保留下来。

  接下里看下内存分配的过程

  class MyObject
{
int x;
int y;
int z;
}
static void Main(string[] args)
{
var x = new MyObject();
}

了解汇编语言的都知道这一流程,

MOV指令是数据传送指令,其他的大家可以自行去查约。大致是说,把类的方法表指针拷贝到ecx(计数暂存器)中,作为new ()的参数,调用new,把返回值(对象的地址)拷贝到寄存器中。这里大家大概了解一下就可以了

基本运作方式

在托管进程中存在两种内存堆(本机堆和托管堆),这里我们说下托管堆,本机堆大大家可以自行了解下,

CLR在托管堆(Managed Heap)上为所有的.NET托管对象分配内存,也称之为GC堆,因为其中的对象都要受到垃圾回收机制的控制。

  托管堆又分为两种,小对象堆和大对象堆(LOH),都拥有自己的内存段(Segment[ˈseɡmənt]).内存段的大小视配置和硬件环境而定。

小对象堆

  小对象堆有什么?它又是怎么变化的?

  小对象堆的内存堆分为3代,0,1,2代。第0代和第1代总是位于同一个内存段中,第2代可能跨越多个内存段,LOH也可以跨越多个内存段。包含第0代和第1代堆的内存段被称为暂时段(Ephemeral [ɪˈfemərəl] Segment ˈseɡmənt])

  小对象堆中分配内存的对象生存期。如果 对象小于85000字节,CLR会把它分配在小对象堆中的第0代,通常紧挨当前已用内存空间往后分配,如果扩大内存堆时超越了内存段的边界,则就会触发垃圾回收过程。

  对象总是诞生于第0代内存堆中,只要对象保持存活,每当发生垃圾回收时,GC都会把他提升一代。第0代和第1代内存堆的垃圾回收有时候被称为瞬时回收(Ephemeral Collection)

  在垃圾回收的时候,可能会进行碎片整理(Compaction),也及时GC把对象物理迁移到新的位置中去,以便让内存段中的空闲空间能够连续起来使用。如果为发生碎片整理,那就只需要重新调整各块内存的边界。以下是经历几次未做碎片整理的垃圾回收之后,内存堆的分布可能如下

对象的位置没有移动,但是各代的内存堆的边界发生了变化。

  如果对象到达第2代内存堆,它就会一直留在哪里直至终结。但不代表第2代内存堆只会一直变大, 如果第2代内存堆中的对象都终结了,整个内存段有没有存活[2]的对象,垃圾回收器会把整个内存段交换给操作系统,或者作为其他几代内存堆的附加段,在进行完全垃圾回收(Full Garbge Collection)时,就会可能发生第2代内存堆的回收。

[2]:存活:GC能通过任一已知的GC跟对象(Root),沿着层层引用访问到某个对象,那么它是存活的。GC的根对象可以是程序中的静态变量,或者某个线程的堆栈被正在运行的方法占用(局部变量)或者GC句柄(比如固定对象的句柄,Pinned Handle),或是终结器队列(Finalizer Queue),有些对象可能没有受GC根对象的引用,但如果位于第2代内存堆中,那么第0代回收是不会清理这些对象的。只有完全垃圾回收才会被清理。

  如果第0代堆即将占满一个内存段,垃圾回收也无法通过碎片整理获取足够空闲内存,那么GC会分配一个新的内存段。如图:

  

如果第2代堆继续变大,就可能会跨越多个内存段。LOH也是。但是无论存在多少内存段,第0代和第1代总是位于同一个内存段中。以后我们找出内存堆中有哪些对象存活时。这些会用到。

LOH

LOH,大于85000字节的对象将自动在LOH中分配内存。没有代的概念,垃圾回收期间也不会自动进行碎片化整理,但是可以人为的碎片整理。

垃圾回收的时候会造成什么影响呢?

  垃圾回收是针对某一代及以下几代内存堆进行的。如果回收1代,就会回收0代。如果发生了第0代或第1代垃圾回收,程序在回收期间就会暂停运行,第二代垃圾回收,有部分回收是在后台线程运行进行的。

垃圾回收的4个阶段

  • 挂起(Suspension)---在垃圾回收发生之前,所有托管线程都被强行中止
  • 标记(Mark)--从GC根对象开始,垃圾回收器沿着所有对象引用进行遍历并把所见对象记录下来
  • 碎片整理(Compact)--将对象重新紧挨着存放并更新所有引用,以便减少内存碎片,在小对象堆中,碎片整理会按需进行,无法控制。在LOH中,碎片整理不会自动进行,可以必要时通知垃圾回收器来上一次。
  • 恢复(Resume)--托管线程恢复运行

在标记阶段,不需要遍历内存堆中的所有对象,只要访问那些需要回收的部分即。比如第0代回收只涉及到第0代内存堆中的对象,第1代回收将会标记第0代和第1代内存中的对象。第2代和完全回收,需要遍历内存堆中的所有存活对象,这一开销很大。

  1:垃圾回收过程的耗时几乎完全取决于所涉及的“代”内存堆中的对象数量,而不是你分配到的对象数量,你分配1棵包含100万个对象的树,只要在下次垃圾回收之前把根对象的引用解除。就不会增加垃圾回收的耗时

  2:只要已分配的内存超过某个内部阀值,就会发生这个代垃圾回收,这个阀值是持续变化的。GC会进行调整。

计数器

高性能-GC的更多相关文章

  1. CODE大全告诉你java是否开始没落了

    CODE大全告诉你java是否开始没落了! 22 岁,对于一个技术人来说可谓正当壮年.但对于一门编程语言来说,情况可能又有不同.各类编程语言横空出世,纷战不休,然而 TIOBE 的语言排行榜上,Jav ...

  2. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少分配率, 最重要的规则,缩短对象的生命周期,减少对象层次的深度,减少对象之间的引用,避免钉住对象(Pinning)

    减少分配率 这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量.你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突. 你需要在设计对象时仔细检 ...

  3. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象

    减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...

  4. [翻译] 编写高性能 .NET 代码--第二章 GC -- 配置选项

    配置选项 在基于"less rope to hang yourself with"思想下,.NET 框架没有给开发提供很多太多的配置选项.但在大多数情况下,GC会跟你的硬件配置,及 ...

  5. [翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区

    避免使用终结器 如果没有必要,是不需要实现一个终结器(Finalizer).终结器的代码主要是让GC回收非托管资源用.它会在GC完成标记对象为可回收后,放入一个终结器队列里,在由另外一个线程执行队列里 ...

  6. [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化

    将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...

  7. C#高性能TCP服务的多种实现方式

    哎~~ 想想大部分园友应该对 "高性能" 字样更感兴趣,为了吸引眼球所以标题中一定要突出,其实我更喜欢的标题是<猴赛雷,C#编写TCP服务的花样姿势!>. 本篇文章的主 ...

  8. 使用Ring Buffer构建高性能的文件写入程序

    最近常收到SOD框架的朋友报告的SOD的SQL日志功能报错:文件句柄丢失.经过分析得知,这些朋友使用SOD框架开发了访问量比较大的系统,由于忘记关闭SQL日志功能所以出现了很高频率的日志写入操作,从而 ...

  9. 【腾讯Bugly干货分享】微信mars 的高性能日志模块 xlog

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/581c2c46bef1702a2db3ae53 Dev Club 是一个交流移动 ...

随机推荐

  1. Nginx笔记总结十二:nginx版本号隐藏

    vim nginx.conf http { server_tokens off;} php-fpm fastcgi.conf或fcgi.conf fastcgi_param SERVER_SOFTWA ...

  2. mybatis 添加后获得该新增数据自动生成的 id

    // useGeneratedKeys默认值为false,keyProperty的值对应的是User类中的主键字段名 // mybatis 写法如下 <insert id="inser ...

  3. ajax async异步

    async默认的设置值为true,这种情况为异步方式,就是说当ajax发送请求后,在等待server端返回的这个过程中,前台会继续执行ajax块后面的脚本,直到server端返回正确的结果才会去执行s ...

  4. Linux内核初探 之 进程(三) —— 进程调度算法

    一.基本概念 抢占 Linux提供抢占式多任务,基于时间片和优先级对进程进行强制挂起 非抢占的系统需要进程自己让步(yielding) 进程类型 IO消耗型 经常处于可运行态,等待IO操作过程会阻塞 ...

  5. 《和莎莫的 500 天》中为什么 Summer 最终没有和 Tom 在一起?

    好的电影总是需要仔细赏味几次,每次也都会有不同的收获.就像我钟爱的[500 days of summer]. 彪悍的大胡子导演MarcWebb实在是太有趣,把自己的亲身经历搬上大荧幕,因为" ...

  6. 冒泡排序算法(C#、Java、Python、JavaScript、C、C++实现)

    一.介绍 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小.首字母从Z到A)错误就把他们交换过来. 走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排 ...

  7. 使用 GitHub 开源项目申请 IntelliJ License

    一.写在前面 这次要介绍的是通过使用 GitHub 上的开源项目来申请 IntelliJ Pycharm 的正版 License,只需在 GitHub 上准备一个维护超过3个月的开源项目,就能免费使用 ...

  8. 如何用Postman做接口自动化测试

    目录 前言 什么是自动化测试 自动测试测试分类 为什么需要自动化测试 Postman自动化测试演示 1.新建集合 2.新建接口 3.填写自动化测试脚本 4.录入所有接口 5.执行自动化测试 前言 什么 ...

  9. Cisco模拟器的基本使用

    获取帮助查找命令 只需输入一个'?'便可得到详细的帮助信息,如果想获取c开头的命名,那么直接输入'c?'即可. 在各个模式下切换的方法 给如图所示路由器接口配置IP地址 第一步:安装HWIC-2T(串 ...

  10. 前端javascript知识(一)

    介绍一下 JS 的基本数据类型. Undefined.Null.Boolean.Number.String 介绍一下 JS 有哪些内置对象. Object 是 JavaScript 中所有对象的父对象 ...