【问题标题】:Out of Memory - List with ten million items内存不足 - 包含一千万个项目的列表
【发布时间】:2015-02-17 03:23:18
【问题描述】:

我正在尝试对包含密码的大约一千万行的文本文件执行一些分析。我这样做是通过读取文件的每一行,以该值作为参数创建一个类,然后将该类添加到列表中。在第 4,000,000 行之后,出现内存不足异常。除了将所有内容都存储在 SQL 数据库中之外,还有什么可以做的吗?

编辑:我要做的是获取密码,将其添加到 Credential 对象,然后将其添加到列表中。

public class Credential
    {
        public string Password { get; set; }

        public static readonly List<string> specialCharacters = new List<string> { "@", "!", "~", "*", "^", "&", "\\", "/", "#", "$", "%", "<", ">", ".", ",", "?", ")", "(", "'", "\"", "+", "=", "_", "-", ";", ":", "{", "}", "]", "[", };

        public Credential(string password)
        {
            this.Password = password;
            this.Mapping = new Dictionary<int, CredentialValueType>();
            for (var i = 0; i < this.Length; i++)
            {
                this.Mapping.Add(i, new CredentialValueType(this.Password[i]));
            }
        }


        public Dictionary<int, CredentialValueType> Mapping { get; private set; }

        public int Length
        {
            get
            {
                return this.Password.Length;
            }
        }
        public bool HasUppercase
        {
            get
            {
                return this.Password.Any(c => char.IsUpper(c));
            }
        }
        public bool HasLowercase
        {
            get
            {
                return this.Password.Any(c => char.IsLower(c));
            }
        }
        public bool HasNumber
        {
            get
            {
                return this.Password.Any(c => char.IsNumber(c));
            }
        }
        public bool HasSpecialCharacter
        {
            get //Verify that this works right...
            {
                return this.Password.Where(a => specialCharacters.Contains(a.ToString())).Count() > 0;
            }
        }
    }

public struct CredentialValueType
{
    public char Value { get; set; }
    public ValueType ValueType { get; set; }

    public CredentialValueType(char val)
    {
        this = new CredentialValueType();
        this.Value = val;
        if (char.IsUpper(val)) this.ValueType = ValueType.UpperCase;
        else if (char.IsLower(val)) this.ValueType = PasswordStats.ValueType.LowerCase;
        else if (char.IsNumber(val)) this.ValueType = PasswordStats.ValueType.Number;
        else this.ValueType = PasswordStats.ValueType.SpecialCharacter;
    }
}

我的功能如下:

public class PasswordAnalyzer
    {
        public IList<Credential> Credentials { get; private set; }

        public PasswordAnalyzer(string file, int passwordField = 0, Delimiter delim = Delimiter.Comma)
        {
            this.Credentials = new List<Credential>();
            using (var fileReader = File.OpenText(file)) //Verify UTF-8
            {
                using (var csvReader = new CsvHelper.CsvReader(fileReader))
                {
                    csvReader.Configuration.Delimiter = "\t";
                    while (csvReader.Read())
                    {
                        var record = csvReader.GetField<string>(passwordField);
                        this.Credentials.Add(new Credential(record));
                        System.Diagnostics.Debug.WriteLine(this.Credentials.Count);
                    }
                }
            }
        }
    }

【问题讨论】:

  • 你的实际代码是什么?你在使用 File.ReadLines() 吗?
  • 这似乎解决了一个类似的问题:stackoverflow.com/questions/27561324/…
  • 购买更多内存。或者以增量方式进行分析(例如一次 1M)。
  • 1.获取更多内存。 2、可以批量加工吗? 3. 如果进行任何类型的聚合,请在处理时进行聚合(将总和和计数存储在单独的变量中,并在执行过程中递增以避免将所有内容加载到内存中) 4. 有关您尝试执行的分析类型的更多详细信息会有所帮助.我们只是在黑暗中刺伤。
  • @Pierre-LucPineault 获得更多 RAM 不会神奇地为 32 位进程提供更多地址空间...

标签: c# out-of-memory


【解决方案1】:

您可以将映射存储在一个数组中,而不是创建 400 万个字典。我敢肯定这会节省很多空间,但是如果没有关于消耗多少内存等的更多信息,很难判断这是否能解决您的问题。

我假设您显示的代码不是您的实际代码,但如果您只需要遍历这些行,请使用 IEnumerable,并产生每个结果。由于一次在内存中只有一个“行”,因此您的内存要求会更好。

【讨论】:

  • 这里不仅有 400 万个字典的开销,而且每个密码中的每个字符都有一个字典条目。如果我们平均有 8 个字符/密码,那就是 3200 万个条目。每个条目至少包含 4 个字节的密钥和另一个字节的类型,毫无疑问填充到 4 个字节。我不知道字典的内部结构,但我宁愿怀疑它指向数据——另外 4 个字节。我们至少已经达到 384 兆字节。我确定分配的块跟踪使用的更多,但我不知道详细信息。
【解决方案2】:

如果您将 [Serializable] 属性添加到您的 CredentialCredentialValueType 类中,您可以将它们的状态存储在文件流中而不是内存列表中。

[Serializable]
public class Credential
{
    //code omitted
}

[Serializable]
public class CredentialValueType
{
    //code omitted
}

存储您的凭证对象。

var binFormatter = new Runtime.Serialization.Formatters.Binary.BinaryFormatter();

// Open a file stream to write the objects into
using (var fs = new FileStream(@"C:\temp.dat", FileMode.Create))
{

    // Begin looping through your file here.

    // Get a line from the file and convert it to an object
    var credential = new Credential(line);

    // Serialize your Credential object onto the stream.
    binFormatter.Serialize(fs, credential);

    // End looping through your file here

    // Ensure the buffer is flushed before closing the stream.
    fs.Flush();
}

现在您的凭证对象可以一次处理一个,方法是读回并反序列化它们。

var binFormatter = new Runtime.Serialization.Formatters.Binary.BinaryFormatter();

using (var fs = new FileStream(@"C:\temp.dat", FileMode.OpenOrCreate, FileAccess.Read))
{
    do
    {
        // Deserialize the credential from the file stream
        var credential = (Credential)binFormatter.Deserialize(fs);

        // Process the credential here

    // Loop until the end of the file
    } while (fs.Position < fs.Length - 1)

}

我一直在使用它在内存小于文件大小的机器上创建几 GB 大小的提要文件。

缺点是您不能将Credential 对象列表上的LINQ 与此技术结合使用。但是,如果您知道要扫描的内容的类型,则可以优化您的流程,这样您只需循环一次 CSV 文件,然后您可以多次循环您的 Credential 对象以查找您的数据正在寻找。

【讨论】:

    猜你喜欢
    • 2011-05-26
    • 2023-03-19
    • 2019-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多