关于.net的垃圾回收和大对象处理_标记

CLR垃圾回收器根据所占空间大小划分对象。大对象和小对象的处理方式有很大区别。比如内存碎片整理 —— 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪些潜在的影响。

  大对象堆和垃圾回收

  在.Net 1.0和2.0中,如果一个对象的大小超过85000byte,就认为这是一个大对象。这个数字是根据性能优化的经验得到的。当一个对象申请内存大小达到这个阀值,它就会被分配到大对象堆上。这意味着什么呢?要理解这个,我们需要理解.Net垃圾回收机制。

  如大多人所知道的,.Net GC是按照“代”来回收的。程序中的对象共有3代,0代、1代和2代,0代是最年轻的对象,2代对象存活的时间最长。GC按代回收垃圾也是出于性能考虑的;通常的对象都会在0代是被回收。例如,在一个asp.net程序中,和每一个请求相关的对象都应该在请求结束时回收掉。而没有被回收的对象会成为1代对象;也就是说1代对象是常驻内存对象和马上消亡对象之间的一个缓冲区。

  从代的角度看,大对象属于2代对象,因为只有在2代回收时才会处理大对象。当某代垃圾回收执行时,会同时执行更年轻代的垃圾回收。比如:当1代垃圾回收时会同时回收1代和0代的对象,当2代垃圾回收时会执行1代和0代的回收.

  代是垃圾回收器区分内存区域的逻辑视图。从物理存储角度看,对象分配在不同的托管堆上。一个托管堆(managed heap)是垃圾回收器从操作系统申请的内存区(通过调用windows api VirtualAlloc)。当CLR载入内存之后,会初始化两个托管堆,一个大对象堆(LOH –large object heap)和一个小对象对(SOH – small object heap)。

  内存分配请求就是将托管对象放到对应的托管堆上。如果对象的大小小于85000byte,它会被放置在SOH;否则会被放在LOH上。

  对于SOH,对象在执行一次垃圾回收之后,会进入到下一代。也就是说如果在第一次执行垃圾回收时,存活下来的对象会进入第二代,如果在第2次垃圾回收之后该对象仍然没有被当作垃圾回收掉,它就会成为2代对象;2代对象就是最老的对象不会在提升代数。

当触发垃圾回收时,垃圾回收器会在小对象堆做碎片整理,将存活下来的对象移动到一起。而对于大对象堆,由于移动内存的开销很大,CLR团队选择只是清除它们,将回收掉的对象组成一个列表,以便满足下次有大对象申请使用内存,相邻的垃圾对象会被合并成一块空闲的内存块。

 需要时时留意的是,直到.Net 4.0中也不会对大对象堆做碎片整理操作,将来也许会做。因此如果你要分配大对象并不想他们被移动,你可以使用fixed语句。

  如下小对象堆SOH的回收示意图

.Net 垃圾回收和大对象处理

  上图中第一次垃圾回收之前有四个对象obj0-3;在第一垃圾回收之后obj1和obj3被回收了,同时obj2和obj0移动到一起了;在第二次垃圾回收之前有分配了三个对象obj4-6;在第二次执行垃圾回收之后obj2和obj5被回收了,obj4和obj6被移动到obj0旁边。

下图是大对象堆LOH回收示意图 .Net 垃圾回收和大对象处理

  可以看到在未执行垃圾回收之前,一共有四个对象obj0-3;第一次二代垃圾回收之后obj1和obj2被回收掉了,回收掉之后obj1和obj2所占空间被合并到了一起,在obj4申请分配内存时就把obj1和obj2回收后释放的空间分配给它了;同时留下了一块内存碎片。如果这个碎片的大小小于85000byte,那么这个碎片就在这个程序的生命周期中永远不能被再次利用了。

  如果大对象堆上没有足够的空闲内存容纳要申请的大对象空间,CLR首先会尝试向操作系统申请内存,如果申请失败,就会触发一次二代回收来尝试释放一些内存。

  在2代垃圾回收时,可以将不需要的内存通过VirtualFree交还给操作系统。交还的过程参见下图:

.Net 垃圾回收和大对象处理

  什么时候回收大对象呢?

  在讨论什么时候回收大对象之前先来看下普通的垃圾回收操作什么时机执行吧。垃圾回收在下列情况下发生:

  1. 申请的空间超过0代内存大小或者大对象堆的阀值,多数的托管堆垃圾回收在这种情况下发生

  2. 在程序代码中调用GC.Collect方法时;如果在调用GC.Collect方法是传入GC.MaxGeneration参数时,会执行所有代对象的垃圾回收,包括大对象堆的垃圾回收

  3. 操作系统内存不足时,当应用程序收到操作系统发出的高内存通知时

  4. 如果垃圾回收算法认为做二代回收是有收效时会触发二代垃圾回收

  5. 每一代对象堆的都有一个所占空间大小阀值的属性,当你分配对象到某一代,你增长了内存总量接近了该代的阀值,或者分配对象导致这一代的堆大小超过了堆阀值,就会发生一次垃圾回收。因此当你分配小对象或者大对象时,会对应消耗0代堆或者大对象堆的阀值。当垃圾回收器将对象代数提升到1代或者2代时,会消耗1、2代的阀值。在程序运行中这些阀值是动态变化的。

  大对象堆性能影响

  让我们先看下分配大对象的代价。 CLR为每个新对象分配内存时都要保证这些内存清空的,是没有被其他对象使用的(I give out is cleared)。这就意味着分配的代价完全被清理(clearing)的代价控制着(除非在分配时触发了一次垃圾回收)。如果清空1byte需要2个周期(cycles),就意味着清除一个最小的大对象需要170,000个周期。通常情况下人们不会分配超大的对象,比如说在2GHz的机器上分配16M大小的对象,大约需要16ms来清空内存。这代价太大了。

  让我们在看下回收的代价。前面提到过,大对象和2代龄对象一起回收。如果大对象或者2代对象占用空间超过其阀值时,就会触发2代对象的回收。如果2代回收因为大对象堆超过阀值被触发,2代对象堆本身没有多少对象可以做回收。如果在2代堆上没有多少对象,这问题不大。但是如果2代堆很大对象很多,过多的2代回收就会导致性能问题。如果是临时性的分配大对象,就需要很多的时间来运行垃圾回收;也就是说如果你持续的使用大对象然后又释放大对象对性能会有很大的负面影响。

  大对象堆上的巨大对象通常是数组(很少有一个对象很大的情况)。如果对象中的元素是强引用,代价会很高;如果元素之间没有相互引用,垃圾回收时就不需要遍历整个数组。例如:用一个数组来保存二叉树的节点,一种方法是在节点中强引用左右节点:

?
1
2
3
4
5
6
7
8
class Node
{
Data d;
Node left;
Node right;
}
Node[] binaryTree = new Node[num_nodes];
如果num_nodes是一个很大的数字,就意味着每个节点都至少需要查看二个引用元素。一种替代方案是在节点中保存左右节点元素的数组索引号
?
1
2
3
4
5
6
class Node
{
Data d;
uint left_index;
uint right_index;
}
这样的话,元素之间的引用关系去掉了;可以通过binaryTree[left_index]来获得引用的节点。垃圾回收器在做垃圾回收时也不需要看相关的引用元素了。

  为大对象堆收集性能数据

  有几种方法可以收集大对象堆相关的性能数据。在我解释这些方法之前,让我们先谈一下为什么需要收集大对象堆相关的性能数据。

  在你开始上搜集某个方面的性能数据时,有可能你已经找到这方面造成性能瓶颈的证据;或者你已经没有找遍了所有方面都没有发现问题。

  在查找性能问题时.Net CLR Memory 性能计数器通常是应该先考虑使用的工具。和LOH相关的计数器有generation 2 collectioins(2代堆收集次数)和large object heap size大对象堆大小。Generation 2 collections显示的是进程启动之后2代垃圾回收操作发生的次数。Large object heap size计数器显示的是当前大对象堆的大小值,包括空闲空间;这个计数器是在每次垃圾回收操作之后做更新,并非每次分配内存都做更新。

  可以参考下图在windows性能计数器中观察.Net CLR Memory相关性能数据

.Net 垃圾回收和大对象处理

  你也可以通过程序查询这些计数器的值;很多人通过程序的方式收集性能计数器来帮助查找性能瓶颈。

  当然也可以使用调试器winddbg观察大对象堆。

  最后提示一下:到目前为止,大对象堆作为垃圾回收的一部分是不做内存碎片整理的,但是这个只是一个clr的实现细节,程序代码不应该依赖这个特点。如果要确保对象不会被垃圾回收器移动,就要使用fixed语句。

转载于:https://www.cnblogs.com/One-dream-man/archive/2012/12/18/2824217.html

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

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

相关文章

三篇文章了解 TiDB 技术内幕——说存储

此文转载了 申砾 PingCAP 的文章:https://mp.weixin.qq.com/s?__bizMzI3NDIxNTQyOQ&mid2247484822&idx1&sn5434362800d8dcc0ca69d2f3f3260173&chksmeb1622fcdc61abea428f74b26a24bc589d524dd3b666d9b124809300f488d00b33a315a87792&scene21#we…

复制数组方法总结

为什么80%的码农都做不了架构师&#xff1f;>>> 在java中&#xff0c;对数组复制有多种 1.通过循环来复制 比如用for循环 int a[]{1,2,3}; int b[]new int[a.length]; for(int i0;i<a.length;i){ b[i]a[i]; } 2.直接复制 int a[]{1,2,3}; int b[]a…

JS/Cs相互调用

js调用cs中函数的方法 在前台js代码里写上<%method();%>举例:cs文件中写的有public void method(){....执行某些操作.}这个函数,然后在前台页面的js里面调用.<script type"text/javascript"><%method();%></script>在cs中调用js函数法一:C…

PostgreSQL体系架构

PostgreSQL 使用客户机/服务器&#xff08;C/S&#xff09;的模式提供服务&#xff0c;一个PostgreSQL会话由下列相关的进程&#xff08;程序&#xff09;组成&#xff1a; (1)一个服务器端进程。该进程管理数据库文件&#xff0c;接受客户端与数据库的连接&#xff0c;且代表…

IPC之九:使用UNIX Domain Socket进行进程间通信的实例

socket 编程是一种用于网络通信的编程方式&#xff0c;在 socket 的协议族中除了常用的 AF_INET、AF_RAW、AF_NETLINK等以外&#xff0c;还有一个专门用于 IPC 的协议族 AF_UNIX&#xff0c;IPC 是 Linux 编程中一个重要的概念&#xff0c;常用的 IPC 方式有管道、消息队列、共…

CH 5102Mobile Service题解

题目&#xff1b; 用动态规划很容易将完成任务量作为dp的阶段&#xff0c;通过指派服务员&#xff0c;从当前i-1个任务转移到i个任务&#xff1b; 我们可以用一个四维数组f[i][x][y][z]来表示在完成当前任务i时&#xff0c;三个机器人分别在x&#xff0c;y&#xff0c;z的位置&…

Postgres主进程文件—postmaster.pid

postmaster内容 使用cat -n 命令可以查看postmaster.pid文件内容&#xff1a; ) 根据每一行进行解释&#xff0c;并给出对应的源代码说明 13795: 代表Postgres主进程的PID/usr/local/pgsql/data: 代表数据目录 1529235109&#xff1a; 代表postmaster文件的创建时间。 54…

Mysql 中 delete 与 left join 的问题

今天在一个程序后台删除一个东西的时候&#xff0c;却出现了这个问题&#xff1a; 在Google搜索了大约1小时候&#xff0c;终于找到了原因&#xff0c;解决起来非常简单&#xff1a; 增加一个T.*就搞定了。 故障分析&#xff1a;因为Insert、Update、Delete三个参数&#xff0c…

web架构设计经验分享

2019独角兽企业重金招聘Python工程师标准>>> 本人作为一位web工程师&#xff0c;着眼最多之处莫过于 性能与架构&#xff0c;本次幸得参与sd2.0大会&#xff0c;得以与同行广泛交流,于此二方面&#xff0c;有些心得&#xff0c;不敢独享&#xff0c;与众博友分享&am…

面向对象与软件工程—团队作业1

一、队伍介绍 队伍名称&#xff1a;逍遥此身君子意 队伍编号&#xff1a;1523933 参赛区域&#xff1a;西北赛区 参赛类别&#xff1a;小程序 指导老师&#xff1a;崔亚超 二、队伍成员信息 姓名&#xff1a;凌龙&#xff08;队长&#xff09; 学号&#xff1a;1700802085 班级…

PostgreSQL的核心架构

PostgreSQL的核心架构 注意 本人的博客都迁移到本人自己搭建的博客地址&#xff0c;通过此处可查看。 应用程序的访问接口 1. 访问接口总体图 进程及内存结构 1. 进程和内存结构图 主进程&#xff1a;Postmaster进程 辅助进程&#xff1a;SysLogger&#xff08;系统日志&a…

Linux 下的多线程下载工具

2019独角兽企业重金招聘Python工程师标准>>> 最先用的是 Axel&#xff08;http://axel.alioth.debian.org/&#xff09;&#xff0c;功能还可以&#xff0c;不过下载文件最多支持到 2GB&#xff0c;再大的文件就不能下载了&#xff0c;真变态&#xff01; aget&…

Launcher结构之home screen

今天刚刚知道如果你的Eclipse里面的工程指向服务器里面的源码记住千万不能在Eclipse里编译~~会在服务器上的源码里多处很多的中间件这样make不了只能清除那些中间件才能编译比较麻烦 Home screen可以说是一个手机的最重要应用&#xff0c;就像一个门户网站的首页&#xff0c;直…

分布式事务2PC、3PC模型

工作中使用最多的是本地事务&#xff0c;但是在对单一项目拆分为 SOA、微服务之后&#xff0c;就会牵扯出分布式事务场景 文章以分布式事务为主线展开说明&#xff0c;并且针对 2PC、3PC 算法进行详细的讲解&#xff0c;最后通过一个 Demo 来更深入掌握分布式事务&#xff0c;…

Python学习之路——装饰器

开放封闭原则&#xff1a;不改变调用方式与源代码上增加功能 1.不能修改被装饰对象(函数)的源代码&#xff08;封闭&#xff09; 2.不能更改被修饰对象(函数)的调用方式&#xff0c;且能达到增加功能的效果&#xff08;开放&#xff09;View Code装饰器 # 把要被装饰的函数作为…

通断时间面积法

背景&#xff1a; 来 源&#xff1a; 通断时间面积法是入选《供热计量技术规程》JGJ173-2009的一种热量分摊计量方法实现分户计量的一种计量方法。由清华大学建筑节能研究中心江亿院士提出。 简 称 ( 俗称 )&#xff1a;&#xff08;1&#xff09;“时温法”&#xff08;2&a…

win8 关于Adobe CS6系列软件Patch覆盖失败的问题(Photoshop CS6、Adobe Illustrator CS6、Adobe Fireworks CS6)...

我在Adobe文件夹下安装了Photoshop CS6和 Adobe Illustrator CS6&#xff0c;结果当我为AI覆盖Path文件后&#xff0c;我发现PS和AI全部都打不开了。反复覆盖还是没用。 不过很奇怪fireworks Cs6能用。FW我没有跟PS、AI装在同一个文件夹下。 我想难道是安装目录的问题&#xff…

《集体智慧编程》——第一章导读

为什么80%的码农都做不了架构师&#xff1f;>>> 什么是集体智慧 其含义是指&#xff1a;为了长早新的想法&#xff0c;而将一群人的行为、偏好或思想组合在一起。 完成这项工作的一种最为基础的方法&#xff0c;便是使用调查问卷或普查。从一大群人中搜集的答案可…

在Mono 2.8上部署ASP.NET MVC 2

Mono 2.8发布&#xff1a;C#4.0和更好的性能&#xff0c;我们知道Mono 2.8对ASP.NET MVC 2的完全支持&#xff0c;下面我们就来测试下在Mono 2.8上部署ASP.NET MVC 2应用程序。我的环境是Opensuse 11.3,通过以下命令部署好Mono 2.8的开发环境&#xff0c;之所以说是开发环境是同…

SRV记录注册不成功的可能的原因

1.1.1 SRV记录注册不成功的可能的原因 默认情况&#xff0c;安装完活动目录就会DNS中的SRV记录就注册成功了&#xff0c;如果您在域控制器上重启Netlogon服务&#xff0c;有可能还是不能注册SRV记录到DNS服务器上&#xff0c;以下是总结的需要检查的几点。 DNS区域名字是否正确…