- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
场景:我需要以数学方式处理超过 1.5GB 的文本和 csv 文件。我尝试使用 SQL Server Express,但加载信息,即使使用 BULK 导入也需要很长时间,理想情况下我需要将整个数据集放在内存中,以减少硬盘 IO。
有超过 120,000,000 条记录,但即使我尝试将信息过滤到仅一列(内存中),我的 C# 控制台应用程序也消耗约 3.5GB 的内存来处理仅 125MB(700MB 实际读入)文本。
GC 似乎没有收集对字符串和字符串数组的引用,即使在将所有引用设置为 null 并使用 using 关键字封装 IDisposables 之后也是如此。
我认为罪魁祸首是 String.Split() 方法,它为每个逗号分隔值创建一个新字符串。
您可能会建议我什至不应该将不需要的*列读入字符串数组,但这没有捕获重点:如何将这个 整个 数据集放在内存中,以便我可以处理它在 C# 中是并行的吗?
我可以优化统计算法并使用复杂的调度算法协调任务,但这是我在遇到内存问题之前希望做的事情,而不是因为。
我已经包含了一个完整的控制台应用程序,它可以模拟我的环境并且应该有助于重现问题。
感谢任何帮助。提前致谢。
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace InMemProcessingLeak
{
class Program
{
static void Main(string[] args)
{
//Setup Test Environment. Uncomment Once
//15000-20000 files would be more realistic
//InMemoryProcessingLeak.GenerateTestDirectoryFilesAndColumns(3000, 3);
//GC
GC.Collect();
//Demostrate Large Object Memory Allocation Problem (LOMAP)
InMemoryProcessingLeak.SelectColumnFromAllFiles(3000, 2);
}
}
class InMemoryProcessingLeak
{
public static List<string> SelectColumnFromAllFiles(int filesToSelect, int column)
{
List<string> allItems = new List<string>();
int fileCount = filesToSelect;
long fileSize, totalReadSize = 0;
for (int i = 1; i <= fileCount; i++)
{
allItems.AddRange(SelectColumn(i, column, out fileSize));
totalReadSize += fileSize;
Console.Clear();
Console.Out.WriteLine("Reading file {0:00000} of {1}", i, fileCount);
Console.Out.WriteLine("Memory = {0}MB", GC.GetTotalMemory(false) / 1048576);
Console.Out.WriteLine("Total Read = {0}MB", totalReadSize / 1048576);
}
Console.ReadLine();
return allItems;
}
//reads a csv file and returns the values for a selected column
private static List<string> SelectColumn(int fileNumber, int column, out long fileSize)
{
string fileIn;
FileInfo file = new FileInfo(string.Format(@"MemLeakTestFiles/File{0:00000}.txt", fileNumber));
fileSize = file.Length;
using (System.IO.FileStream fs = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(fs))
{
fileIn = sr.ReadToEnd();
}
}
string[] lineDelimiter = { "\n" };
string[] allLines = fileIn.Split(lineDelimiter, StringSplitOptions.None);
List<string> processedColumn = new List<string>();
string current;
for (int i = 0; i < allLines.Length - 1; i++)
{
current = GetColumnFromProcessedRow(allLines[i], column);
processedColumn.Add(current);
}
for (int i = 0; i < lineDelimiter.Length; i++) //GC
{
lineDelimiter[i] = null;
}
lineDelimiter = null;
for (int i = 0; i < allLines.Length; i++) //GC
{
allLines[i] = null;
}
allLines = null;
current = null;
return processedColumn;
}
//returns a row value from the selected comma separated string and column position
private static string GetColumnFromProcessedRow(string line, int columnPosition)
{
string[] entireRow = line.Split(",".ToCharArray());
string currentColumn = entireRow[columnPosition];
//GC
for (int i = 0; i < entireRow.Length; i++)
{
entireRow[i] = null;
}
entireRow = null;
return currentColumn;
}
#region Generators
public static void GenerateTestDirectoryFilesAndColumns(int filesToGenerate, int columnsToGenerate)
{
DirectoryInfo dirInfo = new DirectoryInfo("MemLeakTestFiles");
if (!dirInfo.Exists)
{
dirInfo.Create();
}
Random seed = new Random();
string[] columns = new string[columnsToGenerate];
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= filesToGenerate; i++)
{
int rows = seed.Next(10, 8000);
for (int j = 0; j < rows; j++)
{
sb.Append(GenerateRow(seed, columnsToGenerate));
}
using (TextWriter tw = new StreamWriter(String.Format(@"{0}/File{1:00000}.txt", dirInfo, i)))
{
tw.Write(sb.ToString());
tw.Flush();
}
sb.Remove(0, sb.Length);
Console.Clear();
Console.Out.WriteLine("Generating file {0:00000} of {1}", i, filesToGenerate);
}
}
private static string GenerateString(Random seed)
{
StringBuilder sb = new StringBuilder();
int characters = seed.Next(4, 12);
for (int i = 0; i < characters; i++)
{
sb.Append(Convert.ToChar(Convert.ToInt32(Math.Floor(26 * seed.NextDouble() + 65))));
}
return sb.ToString();
}
private static string GenerateRow(Random seed, int columnsToGenerate)
{
StringBuilder sb = new StringBuilder();
sb.Append(seed.Next());
for (int i = 0; i < columnsToGenerate - 1; i++)
{
sb.Append(",");
sb.Append(GenerateString(seed));
}
sb.Append("\n");
return sb.ToString();
}
#endregion
}
}
*在程序的整个生命周期中,这些其他列将被需要并按顺序和随机访问,因此每次从磁盘读取都是非常繁重的开销。
**环境说明:4GB DDR2 SDRAM 800、Core 2 Duo 2.5Ghz、.NET Runtime 3.5 SP1、Vista 64。
最佳答案
是的,String.Split 为每个“片段”创建一个新的 String 对象——这就是它的本意。
现在,请记住 .NET 中的字符串是 Unicode(实际上是 UTF-16),加上对象开销,字符串的字节成本大约是 20 + 2*n
其中 n
是字符数。
这意味着如果您有很多小字符串,与所涉及的文本数据的大小相比,它会占用大量内存。例如,将 80 个字符的行拆分为 10 x 8 个字符串将占用文件中的 80 个字节,但 10 * (20 + 2*8) = 360 个字节的内存 - 4.5 倍的爆炸!
我怀疑这是一个 GC 问题 - 我建议您在不需要时删除将变量设置为 null 的额外语句 - 只是数据过多的问题。
我会建议您逐行阅读文件(使用 TextReader.ReadLine()
而不是 TextReader.ReadToEnd()
)。如果不需要,显然将整个文件保存在内存中是一种浪费。
关于c# - 为什么我不能利用计算机中的 4GB RAM 来处理 C# 中少于 2GB 的信息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/704350/
关于我需要什么和我目前拥有什么的简短介绍 我连接到一个数据库并从中获取我的数据,然后我得到 ( Name , LongNumber) 并且基本上我有一个事件( Action )在事件发生时触发(这个
有人可以帮我分解一下吗?为什么不能用两次乘法来完成? 复数的乘法 如果计算所需的乘法次数被视为其难度的衡量标准,并且这些计算是使用复数执行的,那么很自然地会问需要多少次实数乘法才能实现评估复杂产品的实
使用 PHP 的 preg_match() 函数,我将如何匹配 2 到 5 个字符之间的单词?在这种情况下,字母保证是大写的 A-Z,并且每个 $word 变量中只有一个单词。 它必须拒绝一个 6 个
想知道是否可以在 SQL 中选择多于/少于 x 个字符的内容。 例如,我有一个员工表,我想显示姓名中包含超过 4 个字符的所有员工姓名。 这是一个示例表 ID EmpName Dept 1 John
我需要选择相同 order_ID 的所有值,其中第一个日期和最后一个日期之间的差异等于或小于 7,但我不知道我应该怎么做,可以吗给我一些帮助,我应该怎么做? 数据库布局: +-------------
我有“名称”JavaScript 变量。如果变量“名称”包含少于 4 个字符,我想执行行:msg('name','Your name must contain minimum 4 characters
我最大限度地简化了代码以使其可读。我没有对 Bootstrap 文件夹进行任何更改。所以这是“简化的”HTML 页面: 3
我已经为我的网站实现了 PayPal 延迟链接支付,但我只需要延迟 3 天,而不是默认的 90 天。有人可以告诉我是否可以修改 PHP 脚本以指示 3 天,如果可以,请告诉我我在代码中的何处进行了此指
我想在不到 1 毫秒的时间内调用线程休眠。我读到 thread.Sleep 和 Windows 操作系统都不支持它。 解决方案是什么? 对于所有想知道我为什么需要这个的人:我正在进行压力测试,想知道我
我有一个 html 文档,当 URL 长度小于 30 个字符时,我想隐藏其中的一些 id(在本例中为 id="test")。 var test = document.getElementById('t
所以,我创建了一个带有缩略图导航的 slider ,但是当它的幻灯片少于 5 个时,问题就开始出现了。 它不是只显示它应该显示的幻灯片,而是重复幻灯片以填充容器高度,并且垂直缩略图不会在单击时更改幻灯
我希望能够隐藏少于 3 个字符的列表项,我该怎么做?我下面的代码有什么问题? 我是 JavaScript/jQuery 新手。 jQuery().ready(function () { if
常识说,对于足够小的数组,插入排序是最好的。例如,Timsort对最多 64 个元素的数组使用(二进制)插入排序;来自 Wikipedia : Some divide-and-conquer algo
我从 Javascript 对象动态地将数据添加到表中。我有一个代码最终是这样的: 1 1 2 3 1 2 即使 tds 不存在,我也希望每一行都有表格边框。所以基本上对于代码中的示例
vs2005支持::stdext::hash_map::std::map. 然而,在我的测试中,::stdext::hash_map 的插入和删除 OP 似乎比::std::map 慢。(少于 100
我正在尝试使用 ViewPager 组件实现无限轮播。我基于 Antonyt 创建的那个但是使用少于 4 个 View 会出现问题。由于 View 已经到位。必须是一种欺骗 Viewpager 在不同
在这篇关于 falsehoods programmers believe about time 的有趣文章中,其中之一是 Thread.sleep(1000) sleeps for >= 1000 m
我需要编写模拟真实用户交互的加特林场景。它应该偶尔发出一些请求,例如每个用户每小时 10 个(总共 20 个用户)。 根据我在文档中看到的,constantUsersPerSec接受 double ,
我正在尝试使用只有 7 列的网格创建日历。我想让这 7 列均匀分布并适合整行。目前,7 列加起来不等于 12,我得到 12 列,其中 5 列是空的。 Bootstrap 3 中是否有办法让所有 7 个
我有一个 170k 行的 .txt 文件。我正在将 txt 文件导入 Pandas 。 每行都有许多用逗号分隔的值。 我想提取具有 9 个值的行。 我目前正在使用: data = pd.read_cs
我是一名优秀的程序员,十分优秀!