成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

.NET陷阱之奇怪的OutOfMemoryException

開發 后端
首先我們來探討另外一個問題:不考慮非托管內存的使用,在最壞情況下,當系統出現OutOfMemoryException異常時,有效的內存(程序中有GC Root的對象所占用的內存)使用量會是多大呢?

我們在開發過程中曾經遇到過一個奇怪的問題:當軟件加載了很多比較大規模的數據后,會偶爾出現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/)。

  1. public class Program  
  2.  {  
  3.      static void Main(string[] args)  
  4.      {  
  5.          var smallBlockSize = 90000;  
  6.          var largeBlockSize = 1 << 24;  
  7.          var count = 0;  
  8.          var bigBlock = new byte[0];  
  9.          try 
  10.          {  
  11.              var smallBlocks = new List<byte[]>();  
  12.              while (true)  
  13.              {  
  14.                  GC.Collect();  
  15.                  bigBlock = new byte[largeBlockSize];  
  16.                  largeBlockSize++;  
  17.                  smallBlocks.Add(new byte[smallBlockSize]);  
  18.                  count++;  
  19.              }  
  20.          }  
  21.          catch (OutOfMemoryException)  
  22.          {  
  23.              bigBlock = null;  
  24.              GC.Collect();  
  25.              Console.WriteLine("{0} Mb allocated",   
  26.                  (count * smallBlockSize) / (1024 * 1024));  
  27.          }  
  28.            
  29.          Console.ReadLine();  
  30.      }  
  31.  } 

這段代碼不斷的交替分配一個較小的數組和一個較大的數組,其中較小數組的大小為90, 000字節,而較大數組的大小從16M字節開始,每次增加一個字節。如代碼第15行所示,在每一次循環中bigBlock都會引用新分配的大數組,從而使之前的大數組變成可以被垃圾回收的對象。在發生OutOfMemoryException時,實際上代碼會有count個小數組和一個大小為 16M + count 的大數組處于有效狀態。最后代碼輸出了異常發生時小數組所占用的內存總量。

下面是在我的機器上的運行結果——和你的預測有多大差別?提醒一下,如果你要親自測試這段代碼,而你的機器是64位的話,一定要把生成目標改為x86。

  1. 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++的堆中創建對象要快很多——當然,是在后者不使用額外的內存管理模塊的情況下。】

在對某個Generation進行垃圾回收時,運行時會先標記所有可以從有效引用到達的對象,然后壓縮內存空間,將有效對象集中到一起,而合并已回收的對象占用的空間,如下圖所示。

但是,問題就出在上面特別標出的“一般情況”之外。.NET會將對象分成兩種情況區別對象,一種是大小小于85, 000字節的對象,稱之為小對象,它就對應于前面描述的一般情況;另外一種是大小在85, 000之上的對象,稱之為大對象,就是它造成了前面示例代碼中內存使用率的問題。在.NET中,所有大對象都是分配在另外一個特別的連續內存(LOH, Large Object Heap)中的,而且,每個大對象在創建時即屬于G2,也就是說只有在進行Generation 2的垃圾回收時,才會處理LOH。而且在對LOH進行垃圾回收時不會壓縮內存!更進一步,LOH上空間的使用方式也很特殊——當分配一個大對象時,運行時會優先嘗試在LOH的尾部進行分配,如果尾部空間不足,就會嘗試向操作系統請求更多的內存空間,只有在這一步也失敗時,才會重新搜索之前無效對象留下的內存空隙。如下圖所示:

從上到下看

1.LOH中已經存在一個大小為85K的對象和一個大小為16M對象,當需要分配另外一個大小為85K的對象時,會在尾部分配空間;

2.此時發生了一次垃圾回收,大小為16M的對象被回收,其占用的空間為未使用狀態,但運行時并沒有對LOH進行壓縮;

3.此時再分配一個大小為16.1M的對象時,分嘗試在LOH尾部分配,但尾部空間不足。所以,

4.運行時向操作系統請求額外的內存,并將對象分配在尾部;

5.此時如果再需要分配一個大小為85K的對象,則優先使用尾部的空間。

所以前面的示例代碼會造成LOH變成下面這個樣子,當最后要分配16M + N的內存時,因為前面已經沒有任何一塊連續區域滿足要求時,所以就會引發OutOfMemoryExceptiojn異常。

 

要解決這一問題其實并不容易,但可以考慮下面的策略。 

#p#

1.將比較大的對象分割成較小的對象,使每個小對象大小小于85, 000字節,從而不再分配在LOH上;

2.盡量“重用”少量的大對象,而不是分配很多大對象;

3.每隔一段時間就重啟一下程序。

最終我們發現,我們的軟件中使用數組(List<float>)保存了一些曲線數據,而這些曲線的大小很可能會超過了85, 000字節,同時曲線對象的個數也非常多,從而對LOH造成了很大的壓力,甚至出現了文章開頭所描述的情況。針對這一情況,我們采用了策略1的方法,定義了一個類似C++中deque的數據結構,它以分塊內存的方式存儲數據,而且保證每一塊的大小都小于85, 000,從而解決了這一問題。

此外要說的是,不要以為64位環境中可以忽略這一問題。雖然64位環境下有更大的內存空間,但對于操作系統來說,.NET中的LOH會提交很大范圍的內存區域,所以當存在大量的內存空隙時,即使不會出現OutOfMemoryException異常,也會使得內頁頁面交換的頻率不斷上升,從而使軟件運行的越來越慢。

最后分享我們定義的分塊列表,它對IList<T>接口的實現行為與List<T>相同,代碼中只給出了比較重要的幾個方法。

  1. public class BlockList<T> : IList<T>  
  2.  {  
  3.      private static int maxAllocSize;  
  4.      private static int initAllocSize;  
  5.      private T[][] blocks;  
  6.      private int blockCount;  
  7.      private int[] blockSizes;  
  8.      private int version;  
  9.      private int countCache;  
  10.      private int countCacheVersion;  
  11.    
  12.      static BlockList()  
  13.      {  
  14.          var type = typeof(T);  
  15.          var size = type.IsValueType ? Marshal.SizeOf(default(T)) : IntPtr.Size;  
  16.          maxAllocSize = 80000 / size;  
  17.          initAllocSize = 8;  
  18.      }  
  19.    
  20.      public BlockList()  
  21.      {  
  22.          blocks = new T[8][];  
  23.          blockSizes = new int[8];  
  24.          blockCount = 0;  
  25.      }  
  26.    
  27.      public void Add(T item)  
  28.      {  
  29.          int blockId = 0, blockSize = 0;  
  30.          if (blockCount == 0)  
  31.          {  
  32.              UseNewBlock();  
  33.          }  
  34.          else 
  35.          {  
  36.              blockId = blockCount - 1;  
  37.              blockSize = blockSizes[blockId];  
  38.              if (blockSize == blocks[blockId].Length)  
  39.              {  
  40.                  if (!ExpandBlock(blockId))  
  41.                  {  
  42.                      UseNewBlock();  
  43.                      ++blockId;  
  44.                      blockSize = 0;  
  45.                  }  
  46.              }  
  47.          }  
  48.    
  49.          blocks[blockId][blockSize] = item;  
  50.          ++blockSizes[blockId];  
  51.          ++version;  
  52.      }  
  53.    
  54.      public void Insert(int index, T item)  
  55.      {  
  56.          if (index > Count)  
  57.          {  
  58.              throw new ArgumentOutOfRangeException("index");  
  59.          }  
  60.    
  61.          if (blockCount == 0)  
  62.          {  
  63.              UseNewBlock();  
  64.              blocks[0][0] = item;  
  65.              blockSizes[0] = 1;  
  66.              ++version;  
  67.              return;  
  68.          }  
  69.    
  70.          for (int i = 0; i < blockCount; ++i)  
  71.          {  
  72.              if (index >= blockSizes[i])  
  73.              {  
  74.                  index -= blockSizes[i];  
  75.                  continue;  
  76.              }  
  77.    
  78.              if (blockSizes[i] < blocks[i].Length || ExpandBlock(i))  
  79.              {  
  80.                  for (var j = blockSizes[i]; j > index; --j)  
  81.                  {  
  82.                      blocks[i][j] = blocks[i][j - 1];  
  83.                  }  
  84.    
  85.                  blocks[i][index] = item;  
  86.                  ++blockSizes[i];  
  87.                  break;  
  88.              }  
  89.    
  90.              if (i == blockCount - 1)  
  91.              {  
  92.                  UseNewBlock();  
  93.              }  
  94.    
  95.              if (blockSizes[i + 1] == blocks[i + 1].Length  
  96.                  && !ExpandBlock(i + 1))  
  97.              {  
  98.                  UseNewBlock();  
  99.                  var newBlock = blocks[blockCount - 1];  
  100.                  for (int j = blockCount - 1; j > i + 1; --j)  
  101.                  {  
  102.                      blocks[j] = blocks[j - 1];  
  103.                      blockSizes[j] = blockSizes[j - 1];  
  104.                  }  
  105.    
  106.                  blocks[i + 1] = newBlock;  
  107.                  blockSizes[i + 1] = 0;  
  108.              }  
  109.    
  110.              var nextBlock = blocks[i + 1];  
  111.              var nextBlockSize = blockSizes[i + 1];  
  112.              for (var j = nextBlockSize; j > 0; --j)  
  113.              {  
  114.                  nextBlock[j] = nextBlock[j - 1];  
  115.              }  
  116.    
  117.              nextBlock[0] = blocks[i][blockSizes[i] - 1];  
  118.              ++blockSizes[i + 1];  
  119.    
  120.              for (var j = blockSizes[i] - 1; j > index; --j)  
  121.              {  
  122.                  blocks[i][j] = blocks[i][j - 1];  
  123.              }  
  124.    
  125.              blocks[i][index] = item;  
  126.              break;  
  127.          }  
  128.    
  129.          ++version;  
  130.      }  
  131.    
  132.      public void RemoveAt(int index)  
  133.      {  
  134.          if (index < 0 || index >= Count)  
  135.          {  
  136.              throw new ArgumentOutOfRangeException("index");  
  137.          }  
  138.    
  139.          for (int i = 0; i < blockCount; ++i)  
  140.          {  
  141.              if (index >= blockSizes[i])  
  142.              {  
  143.                  index -= blockSizes[i];  
  144.                  continue;  
  145.              }  
  146.    
  147.              if (blockSizes[i] == 1)  
  148.              {  
  149.                  for (int j = i + 1; j < blockCount; ++j)  
  150.                  {  
  151.                      blocks[j - 1] = blocks[j];  
  152.                      blockSizes[j - 1] = blockSizes[j];  
  153.                  }  
  154.    
  155.                  blocks[blockCount - 1] = null;  
  156.                  blockSizes[blockCount - 1] = 0;  
  157.                  --blockCount;  
  158.              }  
  159.              else 
  160.              {  
  161.                  for (int j = index + 1; j < blockSizes[i]; ++j)  
  162.                  {  
  163.                      blocks[i][j - 1] = blocks[i][j];  
  164.                  }  
  165.    
  166.                  blocks[i][blockSizes[i] - 1] = default(T);  
  167.                  --blockSizes[i];  
  168.              }  
  169.    
  170.              break;  
  171.          }  
  172.    
  173.          ++version;  
  174.      }  
  175.    
  176.      private bool ExpandBlock(int blockId)  
  177.      {  
  178.          var length = blocks[blockId].Length;  
  179.          if (length == maxAllocSize)  
  180.          {  
  181.              return false;  
  182.          }  
  183.    
  184.          length = Math.Min(length * 2, maxAllocSize);  
  185.          Array.Resize(ref blocks[blockId], length);  
  186.          return true;  
  187.      }  
  188.    
  189.      private void UseNewBlock()  
  190.      {  
  191.          if (blockCount == blocks.Length)  
  192.          {  
  193.              Array.Resize(ref blocks, blockCount * 2);  
  194.              Array.Resize(ref blockSizes, blockCount * 2);  
  195.          }  
  196.    
  197.          blocks[blockCount] = new T[initAllocSize];  
  198.          blockSizes[blockCount] = 0;  
  199.          ++blockCount;  
  200.      }  
  201.  } 

原文鏈接:http://www.cnblogs.com/brucebi/archive/2013/04/16/3024136.html

責任編輯:張偉 來源: 博客園
相關推薦

2010-06-03 13:00:37

DNS故障

2014-07-29 09:16:14

Fragment

2009-06-08 20:05:14

Eclipse deb

2022-03-05 00:08:52

網絡安全交易

2025-04-02 00:00:04

2015-08-11 08:51:40

游戲死亡

2022-10-14 09:12:59

參數Python類型

2023-03-14 07:23:48

ReactJSX語法

2025-03-05 10:00:00

.NET 9C#開發

2021-07-08 23:53:44

Go語言拷貝

2021-02-02 16:34:12

useMemo性能優化函數

2018-01-24 18:00:21

LinuxDebianvim

2023-02-08 16:29:58

前后端開發

2020-09-09 07:55:51

TS開源符號

2021-09-11 19:04:38

.NetSoapCore協議

2021-07-11 12:12:49

.NETJWTjson

2009-07-28 17:17:19

ASP.NET概述

2010-10-22 15:45:49

無線互聯

2011-04-14 13:52:27

ArrayList

2023-06-26 00:03:55

Go語言類型
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产成人免费一区二区60岁 | 一区二区三区日韩 | 极情综合网 | 久久精品视频免费观看 | 日韩欧美三区 | 99久久婷婷国产亚洲终合精品 | 蜜桃av一区二区三区 | 能看的av | 免费视频一区 | 国内精品久久久久久影视8 最新黄色在线观看 | 99久久免费观看 | 亚洲成色777777在线观看影院 | 日韩欧美国产一区二区三区 | 亚洲性视频 | 日韩免费视频一区二区 | 午夜天堂精品久久久久 | 精品一二区| 狠狠色综合网站久久久久久久 | 欧洲av一区| 亚洲精品在线91 | 视频在线观看一区 | 国产精品乱码一区二区三区 | 成人精品国产一区二区4080 | 日本aⅴ中文字幕 | 午夜精品一区二区三区在线观看 | 久久久久国产一区二区三区 | 欧美极品视频在线观看 | 神马影院一区二区三区 | 秋霞av国产精品一区 | 高清av电影| www国产精品| 欧美久久久电影 | 雨宫琴音一区二区在线 | 成年人在线视频 | 久久高清免费视频 | 亚洲精品视频免费观看 | 北条麻妃视频在线观看 | 欧美专区在线 | 久久久久中文字幕 | 国产精久久久 | av免费网址 |