编写高性能 .NET 代码 第二章:垃圾回收 基本操作

基本操作

垃圾回收的算法细节还在不断完善中,性能还会有进一步的提升。下文介绍的内容在不同的.NET版本里会略有不同,但大方向是不会有变动的。

在.net进程里会管理2个类型的内存堆:托管和非托管。本地代码申请的,以及由CLR申请的都是非托管内存,使用Windows API 的 VirtualAlloc 方法进行申请。CLR里分配的托管对象则分配在托管堆里,这些对象可以被垃圾回收处理。

在托管堆里有还进一步分为小对象对和大对象堆(LOH)。每个对象类型都有自己的一段堆内存段。每段的大小根据你的配置或者硬件配置有关,对于一个大型的应用,一个内存段可到几百M。

小对象还可以进一步分为3个世代。0代和1代总是在一个内存段里,2代则可以跨越多个内存段。包含0代和1代的内存段称为暂存段。
下图是堆的图形,分别是A段和B段

A段是小对象堆,B段是大对象堆。2代和1代开始只有几个字节大小,因为他们到目前为止是空的。

在小对象堆里分配对象存在一个3世代的生命周期,CLR在小对象堆里分配小于85000字节的对象(8.5k)。0代内存通常在段内存的结尾开始分配。这就是为什么前面看到的.NET内存分配得很快。如果快速分配失败,则在0代边界范围里找一个合适的分配地址。如果也没有合适的位置,则分配器会扩大0代的在内存段里边界范围。如果扩展范围时超过了内存段的范围,则会触发垃圾回收。

对象创建后都是0代。只要对象还存在,在GC时会将它们标记到下一代。0代和1代称为临时集合。

当触发GC时,可能会同时触发压缩,在这种情况下,GC会将对象移动到另外一个内存地址里,释放当前段的内存空间。如果没有触发压缩,也仅仅只是重新划分边界。在一些没有触发过压缩的GC后,结果如下图:

虽然对象没有移动,但边界有重新划分。

压缩可以发生在任何一代的GC里,这是一个相当耗时的过程,因为它需要重新更新对象的引用关系,这需要暂停所有的线程操作。正因为代价高,压缩操作只会在必要的时候才会进行。

一旦对象到达2代,在剩下的生命周期里会一直保持。这并不意味着2代对象会一直存在,如果所有的2代对象被回收,并且整段内存里都没有对象,则这段内存可以被操作系统回收,或者作为其他内存段的附属段。这通常出现在全回收阶段。

那么对象的活着是什么概念呢?在GC时可以通过任何已知的root节点到达对象,能找到对象的引用关系,就说明这个对象还活着。root节点可以是程序里的某个静态变量,线程里正在执行的方法(指局部对象),GC句柄(被pinned的句柄)以及在finalizer queue里的对象。请注意,如果你的对象在2代,并且可以被回收,但你在做0代GC时,它也不会被回收。他们需要等到一个完整回收时才会被干掉。

如果0代对象已经填充满一个内存段,再做压缩也不能获得足够空间时,gc会重新分配一个新的内存段。新的段将用于放置新分配的1代和0代对象。而之前段的对象都将转为2代对象。所有的0代对象转为1代,1代对象同样降为2代对象(因为不需要复制所以很容易实现)。这个新的内存段看起来如下:

如果2代内存不断增加,那么它可以存放在多个内存段里。LOH也一样可以跨越多个内存段存放。但不管有多少个内存段,0代和1代对象始终在一个内存段里。了解到这些知识对后面的学习会很有帮助。大对象堆使用另外一种规则。通常来说一些字符串或者数组,大小在8500直接以上会自动分配到LOH堆里,它不会走上面的代纪模型。出于性能考虑,LOH分配的对象不会在回收的时候做压缩过程,但从.net4.5.1开始,你可以按需做压缩了。就行2代对象那样,当对象不再LOH里需要时,你还是可以回收它使用的内存空间,但稍后我们会说到,在垃圾回收里,最理想的状态是不要将对象创建到大对象堆上。
在LOH里,分配器都是使用空闲列表的方式来寻找最合适的位置来分配,在本章的后面将探索一些技术来减少在大数据堆上减少内存碎片。

垃圾回收特定代时,会顺带回收它下面的代。例如回收1代时,会把0代的也回收。如果是2代,那么就是把所有的都回收了(包括LOH里的)。如果是0代或者1代回收时,程序会暂停执行到GC过程结束。如果是回收2代,这一部分操作可以可能会在另外一个后台线程里执行,这取决于系统配置。

垃圾回收分为4个阶段:

  1. 暂停--所有托管线程需要在回收开始前暂停。

  2. 标记--从每个root节点开始,回收器会对每个对象的引用做标记

  3. 压缩--移动内存对象,并重新修改引用路径以便释放内存碎片。它通常发生在小对象堆上,并在系统认为需要的时候进行,你不能手动控制。在大对象堆上,压缩不会自动发生,但你可以配置垃圾回收器按需压缩他。

  4. 恢复--托管线程回复执行

标记阶段实际上不需要遍历堆上的每个对象,它只会处理需要回收的堆。举个栗子,做0代收集时只会考虑0代的对象,做1代收集时则会标记0代和1代的对象。在做2代或者一次完整回收时,才需要遍历每一个活着的对象,当然这个开销就比较高了。另外需要考虑的是,一个高代的对象可能是低代对象的root节点,这会导致在做遍历时,也会遍历相关的高代对象,当然这个开销会比全回收阶段时小一些。

上面描述的过程会导致以下问题。
首先,垃圾回收所消耗的时间,取决于当前还活着的对象数量,而不是已经分配出去的数量。这就意味着,如果分配了100w个对象在一个root节点上,只要下次GC前,你把它与root切断引用关系,这100w个对象对你的回收耗时不会造成太大影响。
其次,垃圾回收的频率取决于特定的一代里分配了多少内存。一旦超过内部的一个阈值,GC将回收这一代的对象。这个过程GC会根据你的程序做动态调整。如果在某代的回收卓有成效(回收了很多对象),则在这一代的回收会频繁触发,反之则减少。另外一个触发因素就是你的程序在电脑里的可用内存。如果可用内存低于某个阈值,GC也会频繁发生,用来减少堆的总体体积。

从上面的描述里,你可能觉得GC已经超过了你的控制范围。但这其实已经离真相不远了。最简单的优化方式就是,你可以通过控制内存的分配模式来达到。你在了解GC是如何工作后,你可以根据分配速率,对象的生命周期,来选择合适的配置。

相关文章:

  • [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 选择什么来衡量

  • [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 平均值 vs 百分比

  • [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio

  • 编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)

  • 编写高性能 .NET 代码 第二章:垃圾回收

原文地址:http://www.cnblogs.com/yahle/p/6552457.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325224.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《金色梦乡》金句摘抄(一)

System.out.println("《金色梦乡》"); System.out.println("小说类型的书就是比散文类型的书好看"); System.out.println("通俗易懂");,对于描写一个美好圆满的结局有些许抵触樋口晴子和平野晶约在一家荞麦面店见面。迟到的平野晶并无…

如何设计一个高可用的运营系统

转载自 如何设计一个高可用的运营系统 这是一篇来自粉丝的投稿,作者【林湾村龙猫】近一年在做关于运营活动方面的设计。本文是他的关于运营活动的总结,Hollis做了一点点修改。 概述 一个产品业务的发展总是离不开运营二字。随着业务快速的发展以及新…

《金色梦乡》金句摘抄(二)

System.out.println("《金色梦乡》"); System.out.println("小说类型的书就是比散文类型的书好看"); System.out.println("通俗易懂");电视里的播报员看上去像受了伤,声音却十分亢奋。关于爆炸之后的反应,他们的回答基本是…

.Net中的AOP系列之《AOP实现类型》

本系列的实验环境:VS 2017。 读完本章后,可能仍然不能实现自己的AOP工具,但应该对两种主要类型(PostSharp和Castle DynamicProxy)的AOP工具的运行原理有了基本的理解。PostSharp是一个在编译时编织的后期编译器&#x…

JavaFX官方教程(一)之JavaFX概述

翻译自 JavaFX概述 本章概述了可以使用JavaFX API构建的应用程序类型,下载JavaFX库的位置以及有关正在交付的关键JavaFX功能的高级信息。 JavaFX是一组图形和媒体包,使开发人员能够设计,创建,测试,调试和部署在不同平…

《金色梦乡》金句摘抄(三)

System.out.println("《金色梦乡》"); System.out.println("小说类型的书就是比散文类型的书好看"); System.out.println("通俗易懂");他的外貌本就年轻而干练,再加上一副擅长打动人心的好口才教养气质俱佳的金田贞义和纸醉金迷的赌场…

mybatisPlus的分页查询

结论:不是直接limit进行分页的 而是通过rowBounds进行的

相对定位

<!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>.div1{height: 200px;width: 200px;background-color: gray;/*相对定位 相对于自身原来的位置,移开之后,不会释放原点位置*/position: relat…

详解CockroachDB事务处理系统

本文提到的一些术语&#xff0c;比如Serializability和Linearizability&#xff0c;解释看Linearizability, Serializability and Strict Serializability。 本文中观点大部分都是参考了CockroachDB多篇官方blog,设计文档&#xff0c;代码以及相关资料&#xff0c;相对来说比较…

《金色梦乡》金句摘抄(四)

System.out.println("《金色梦乡》"); System.out.println("小说类型的书就是比散文类型的书好看"); System.out.println("通俗易懂");森田森吾的手朝副驾驶座这边伸了过来。空气似乎在某处破裂了&#xff0c;由此而产生的震动转化为波纹&#x…

JavaFX官方教程(二)之JavaFX体系结构

翻译自 JavaFX体系结构 本章提供了JavaFX体系结构和生态系统的高级描述。 图2-1说明了JavaFX平台的架构组件。图中的部分描述了每个组件以及这些部件如何互连。JavaFX公共API下面是运行JavaFX代码的引擎。它由包含JavaFX高性能图形引擎的子组件组成&#xff0c;称为Prism; …

Work Time Manager【开源项目】- 创建自己日志组件 2.0重构

这次我们真是开始来聊聊开源项目里&#xff0c;小而有用的模块或者组件的开发思想。 同时&#xff0c;软件已经更新到1.60的版本了&#xff0c;支持新用户注册&#xff0c;可以不再使用统一的test账户了。 您可以通过以下路径进行下载&#xff1a; 1、在GitHub上fellow一下项目…

《金色梦乡》金句摘抄(五)

System.out.println("《金色梦乡》"); System.out.println("小说类型的书就是比散文类型的书好看"); System.out.println("通俗易懂");路笔直地朝前延伸。腿脚的疲惫和喘息的痛苦让青柳雅春很想瘫坐休息。河里的鱼儿们都带有商标&#xff0c;有本…

JavaFX官方教程(三)之JavaFX示例应用程序入门

翻译自 JavaFX示例应用程序入门 此示例应用程序集旨在帮助您开始使用常见的JavaFX任务&#xff0c;包括使用布局&#xff0c;控件&#xff0c;样式表&#xff0c;FXML和视觉效果。 Hello World&#xff0c;JavaFX Style JavaFX中的表单设计 用CSS设计的花式设计 使用F…

SSH(Spring+Struts2+Hibernate)框架搭建步骤(含配置文件以及运行结果)

1.创建web项目2.导入ssh 所需要的多有jar包&#xff0c;到web-inf下面的lib里面3.将导入过来的jar包都build--path一下4.切换到myeclipse database视图中&#xff0c;添加链接数据库的链接5.新建一个数据库连接&#xff08;如果忘记了数据库链接时你可以去下面的网址中查看&…

Unity 游戏用XLua的HotFix实现热更原理揭秘

本文通过对XLua的HoxFix使用原理的研究揭示出来这样的一套方法。这个方法的第一步&#xff1a;通过对C#的类与函数设置Hotfix标签。来标识需要支持热更的类和函数。第二步&#xff1a;生成函数连接器来连接LUA脚本与C#函数。第三步&#xff1a;在C#脚本编译结束后&#xff0c;使…

外边距

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><style>#div1{border: 1px solid red;background-color: darkseagreen;width: 300px;height: 300px;/*外边距 当前块标签外部的和父级块标签之间的距离…