String引起的OutOfMemory异常 + 如何计算C#对象所占内存的大小

问题:

在一个高并发的接口经常会报错OutOfMemory,检查了代码和服务器各种配置之后感觉一切都正常……

百思不得其解,只能把报错的一段拿出来测试,

最后发现是黄色这段代码出了问题:

 1         public void TestOutOfMemory()
 2         {
 3             var result = string.Empty;
 4             string BSID = "MH_SYS";
 5             string FType = "USR";
 6             DirectoryInfo outFolder = new DirectoryInfo(ConfigurationManager.AppSettings["filePath"]);
 7             var temp = outFolder.GetDirectories().Where(x => !x.Name.Contains("bak"));
 8             if (temp.Count() > 0)
 9             {
10                 try
11                 {
12                     GC.Collect();
13                     GC.WaitForFullGCComplete();
14                     long start = GC.GetTotalMemory(true);
15 
16                     var timeSpan = temp.OrderByDescending(x => x.Name).FirstOrDefault().Name;//获取timeSpan文件夹名称
17                     DirectoryInfo inFolder = new DirectoryInfo(ConfigurationManager.AppSettings["filePath"] + timeSpan + @"\" + BSID + @"\" + FType + @"\");
18                     if (inFolder.GetFiles().Count() > 0)
19                     {
20                         var list = inFolder.GetFiles().OrderBy(x => Convert.ToInt16(x.Name.Split('.')[0]));
21                         foreach (var item in list)
22                         {
23                             using (FileStream fs = new FileStream(item.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
24                             {
25                                 int fsLen = (int)fs.Length;
26                                 byte[] heByte = new byte[fsLen];
27                                 int r = fs.Read(heByte, 0, heByte.Length);
28                                 result += System.Text.Encoding.UTF8.GetString(heByte);
29                             }
30                         }
31                     }
32 
33                     GC.Collect();
34                     GC.WaitForFullGCComplete();
35                     long end = GC.GetTotalMemory(true);
36                     long size = end - start;
37 
38                     Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + "\n" + (size/1024/1024) + "M\n" + result.GetHashCode() + "\n" + result.Substring(0,1000) + "\n\n\n\n");
39                 }
40                 catch (Exception e)
41                 {
42                     throw e;
43                 }
44             }
45         }

用日志记录了下result这个String字符串的哈希编码,发现在多个并发的情况下,都是一样的,说明GC并没有及时回收这个String。

也就是说接口并发时用的都是同一个String对象,加上接口所需要返回的内容很大,每个大概有30M左右,测试当5个并发的时候,占用内存就到了600-700M,10个并发的时候内存占用到了1.5G左右,所以OutOfMemory也不奇怪啦。

PS:计算C#对象所占内存的大小

 请参考上面代码中灰色部分~~

 

解决方案:

找到问题根源之后很简单,只要用StringBuilder代替String,用下面代码替换上文黄色部分即可

StringBuilder result = new StringBuilder();

result.Append(System.Text.Encoding.UTF8.GetString(heByte));

 

posted @ 2019-04-08 17:24  AdolphChen  阅读(2777)  评论(0编辑  收藏  举报