.NET陷阱 五 大对象堆引起的问题与对策

我们在开发过程中曾经遇到过一个奇怪的问题:当软件加载了很多比较大规模的数据后,会偶尔出现OutOfMemoryException异常,但通过内存检查工具却发现还有很多可用内存。于是我们怀疑是可用内存总量充足,但却没有足够的连续内存了——也就是说存在很多未分配的内存空隙。但不是说.NET运行时的垃圾收集器会压缩使用中的内存,从而使已经释放的内存空隙连成一片吗?于是我深入研究了一下垃圾回收相关的内容,最终明确的了问题所在——大对象堆(LOH)的使用。如果你也遇到过类似的问题或者对相关的细节有兴趣的话,就继续读读吧。

如果没有特殊说明,后面的叙述都是针对32位系统。

首先我们来探讨另外一个问题:不考虑非托管内存的使用,在最坏情况下,当系统出现OutOfMemoryException异常时,有效的内存(程序中有GC Root的对象所占用的内存)使用量会是多大呢?2G? 1G? 500M? 50M?或者更小(是不是以为我在开玩笑)?来看下面这段代码(参考 https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/)。

public class Program
 {
     static void Main(string[] args)
     {
         var smallBlockSize = 90000;
         var largeBlockSize = 1 << 24;
         var count = 0;
         var bigBlock = new byte[0];
         try
         {
             var smallBlocks = new List<byte[]>();
             while (true)
             {
                 GC.Collect();
                 bigBlock = new byte[largeBlockSize];
                 largeBlockSize++;
                 smallBlocks.Add(new byte[smallBlockSize]);
                 count++;
             }
         }
         catch (OutOfMemoryException)
         {
             bigBlock = null;
             GC.Collect();
             Console.WriteLine("{0} Mb allocated",
                 (count * smallBlockSize) / (1024 * 1024));
         }   

         Console.ReadLine();
     }
 }

这段代码不断的交替分配一个较小的数组和一个较大的数组,其中较小数组的大小为90, 000字节,而较大数组的大小从16M字节开始,每次增加一个字节。如代码第15行所示,在每一次循环中bigBlock都会引用新分配的大数组,从而使之前的大数组变成可以被垃圾回收的对象。在发生OutOfMemoryException时,实际上代码会有count个小数组和一个大小为 16M + count 的大数组处于有效状态。最后代码输出了异常发生时小数组所占用的内存总量。

下面是在我的机器上的运行结果——和你的预测有多大差别?提醒一下,如果你要亲自测试这段代码,而你的机器是64位的话,一定要把生成目标改为x86。

23 Mb allocated

考虑到32位程序有2G的可用内存,这里实现的使用率只有1%!

下面即介绍个中原因。需要说明的是,我只是想以最简单的方式阐明问题,所以有些语言可能并不精确,可以参考http://msdn.microsoft.com/en-us/magazine/cc534993.aspx以获得更详细的说明。

.NET的垃圾回收机制基于“Generation”的概念,并且一共有G0, G1, G2三个Generation。一般情况下,每个新创建的对象都属于于G0,对象每经历一次垃圾回收过程而未被回收时,就会进入下一个Generation(G0 -> G1 -> G2),但如果对象已经处于G2,则它仍然会处于G2中。

软件开始运行时,运行时会为每一个Generation预留一块连续的内存(这样说并不严格,但不影响此问题的描述),同时会保持一个指向此内存区域中尚未使用部分的指针P,当需要为对象分配空间时,直接返回P所在的地址,并将P做相应的调整即可,如下图所示。【顺便说一句,也正是因为这一技术,在.NET中创建一个对象要比在C或C++的堆中创建对象要快很多——当然,是在后者不使用额外的内存管理模块的情况下。】

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索内存
, 问题
, 五连珠问题
, 对象
, 中创
, outofmemoryexception
, 收集器
一个
中等收入陷阱的对策、hacknet 陷阱、hacknet shell陷阱、.net json 转对象、asp.net内置对象,以便于您获取更多的相关知识。

时间: 2024-08-01 15:38:27

.NET陷阱 五 大对象堆引起的问题与对策的相关文章

.Net 垃圾回收和大对象处理 内存碎片整理

CLR垃圾回收器根据所占空间大小划分对象.大对象和小对象的处理方式有很大区别.比如内存碎片整理 -- 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪些潜在的影响. 大对象堆和垃圾回收 在.Net 1.0和2.0中,如果一个对象的大小超过85000byte,就认为这是一个大对象.这个数字是根据性能优化的经验得到的.当一个对象申请内存大小达到这个阀值,它就会被分配到大对象堆上.这意味着什么呢?要理解这个,我们需要理解.Net垃圾回收机制. 如大多人

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

英文原文:Maoni Stephens,编译:赵玉开(@玉开Sir) CLR垃圾回收器根据所占空间大小划分对象.大对象和小对象的处理方式有很大区别.比如内存碎片整理 -- 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪些潜在的影响. 大对象堆和垃圾回收 在.Net 1.0和2.0中,如果一个对象的大小超过85000byte,就认为这是一个大对象.这个数字是根据性能优化的经验得到的.当一个对象申请内存大小达到这个阈值,它就会被分配到大对象堆上.这

ORACLE中的LOB大对象处理

LOB大对象处理: 主要是用来存储大量数据的数据库字段,最大可以存储4G字节的非结构化数据. 主要介绍字符类型和二进制文件类型LOB数据的存储,单独介绍二进制类型LOB数据的存储. 一.Oracle中的LOB数据类型分类 1.按存储数据的类型分: ①字符类型: CLOB:存储大量 单字节 字符数据. NLOB:存储定宽 多字节 字符数据. ②二进制类型: BLOB:存储较大无结构的二进制数据. ③二进制文件类型: BFILE:将二进制文件存储在数据库外部的操作系统文件中.存放文件路径. 2.按存

CLR全面透彻解析:大型对象堆揭秘

CLR 垃圾回收器 (GC) 将对象分为大型.小型两类.如果是大型对象,与其相关的一些属性将比对象 较小时显得更为重要.例如,压缩大型对象(将内存复制到堆上的其他位置)的费用相当高.在本月的专 栏中,我将深入探讨大型对象堆.我将讨论符合什么条件的对象才能称之为大型对象,如何回收这些大型 对象,以及大型对象具备哪些性能意义. 大型对象堆和 GC 在 Microsoft .NET Framework 1.1 和 2.0 中,如果对象大于或等于 85,000 字节,将被视为大型对象.此数字根据性能优化

【导出导入】导出导入 大对象

1)导出含有lob对象的表 conn  store/store@orcl  SQL> select * from clob_content;  ID  CLOB_COLUMN                                                                     --------------------------------                 1      Creeps in this petty pace             

在java中正确使用PostgreSQL大对象和字节流类型的姿势

有人在java中使用getBlob获取所谓的"大对象"失败,问题是用法不对. groovy:000> rs.getBlob("image") ERROR org.postgresql.util.PSQLException: Bad value for type long : \336\255\276\357\336\255\276\357 at org.postgresql.jdbc2.AbstractJdbc2ResultSet.toLong (Abstra

foreach循环中遍历五个对象之后换行,换行判断怎么写

问题描述 foreach循环中遍历五个对象之后换行,换行判断怎么写 原价:${cloth['costPrice']}元 现价:${cloth['price']}元点击进入 /c:forEach 载我的源代码上面添加,谢谢 解决方案 你的代码在哪里??? 解决方案二: 没有找到你的代码,给你个例子吧. String[] names=new String[]{"a","b","c","d","e","

oracle数据库中的大对象1——永久性的

整理自丁俊老师plsql文档: 1.基本介绍 oracle和pl/sql都支持lob(large object)类型,用来存储大数量数据,如图像文件,声音文件等.oracle 10g r1 支持最大8 到128万一字节的数据存储,依赖于你的数据库的 block size. 在pl/sql中,可以声明的lob的类型变量如下: BFILE :二进制文件,存储在数据库外的操作系统文件,只读的.把此文件当二进制处理. BLOB:二进制大对象,存储在数据库里的大对象,一般是图像声音等文件. CLOB: 字

大数据 堆溢出问题 oracle.sql.CLOB.getChars

问题描述 通过hibernate查询10W级别的数据分批次查询每次100条直接出大对象Object对象里面有几十个属性其中有几个属性对应数据库字段类型为Clob类型有什么好的办法解决.在线等啊Junittest报出的错误debug的参数-Xms256m-Xmx1024mjava.lang.OutOfMemoryError:Javaheapspaceatoracle.jdbc.driver.T4C8TTIClob.read(T4C8TTIClob.java:191)atoracle.jdbc.dr