我有一个非常大(10mb)的csv文件。我解析它并使用通用列表将其放入内存。
我创建了一个代表每一行的类。该类只有几个字段(数据类型为ip-address,string)。
我认为由于文件只有10兆字节,我可以期待内存中的大小相似。
当我发现创建列表的方法是分配300 MB并且没有释放它时,我感到非常惊讶。
这是否正常,以及可能导致这种情况的原因。
请注意,csv文件有很多行(100 000 +),这可能是一个因素。
命名空间Geo 公共类CountryMarker Public StartAddress As IPAddress Public EndAddress As IPAddress 公共国家为字符串 Public CountryCode As String 结束班
Public Class Markers
Private Const DatabasePath = "~/App_Data/ip.csv" '10 MB file
Public Shared List As List(Of CountryMarker) = LoadData()
Shared Function LoadData() As List(Of CountryMarker)
Dim Markers As New List(Of CountryMarker)
Using Stream = New IO.FileStream(Hosting.HostingEnvironment.MapPath(DatabasePath), FileMode.Open)
Dim Reader = New StreamReader(Stream)
Do While Reader.Peek > -1
Dim Line = Reader.ReadLine()
Dim Values = Line.Split(",").Select(Function(i) i.Replace("""", ""))
Markers.Add(New CountryMarker With {.Country = Values(5), .CountryCode = Values(4), .StartAddress = IPAddress.Parse(Values(0)), .EndAddress = IPAddress.Parse(Values(1))})
Loop
End Using
Return Markers
End Function
End Class
结束命名空间
答案 0 :(得分:2)
首先,如果文件是ASCII文本或主要是西欧字符(如英语)的UTF-8,那么文本的内存大小至少是磁盘上文件大小的两倍。 .NET将字符串存储为16位Unicode值。因此,例如,“A”在文本文件中占用一个字节,在内存中需要两个字节。
您创建的每个类实例将需要至少24个字节(16个字节的分配,加上8个字节用于引用。)如果您的文件是100,000行,那么最小为2.4 MB。此外,您分配的每个字符串都需要24个字节,以及字符串所需的任何内容。事情加起来很快。
(请注意,我的24字节编号用于64位系统。在32位运行时中,每个分配为16个字节。)
正如其他人所评论的那样,除非你发布一些代码,包括你的班级定义,否则不可能给你更多细节。
至于不释放任何记忆:这很难证明。也许垃圾收集器还没有进行收集。如果它没有看到内存压力(即有足够的可用内存且没有其他进程请求内存),GC可能会认为它不需要收集。
答案 1 :(得分:0)
除了Jim的评论之外,如果您将大量项目读入List,它将在内部以指数级增加的块大小重新分配内存。我不知道确切的启发式,但考虑到.NET中没有realloc - 如果你使用Reflector,你会发现甚至Array.Resize也会分配一个全新的数组。
假设您分配了2049个对象,并假设List在需要更多空间时将缓冲区大小加倍。你将获得1,2,4 ... 1024,2048,最后是4096 - 几乎是你所需要的两倍(这是最糟糕的情况)。
一种解决方案是调用List.TrimExcess()。这将使阵列恢复到合理的大小。更好的解决方案是估计需要存储的项目数,并将其作为初始容量传递给List构造函数。
但是,如果没有看到解析器和类的代码,很难说这会对您的内存使用问题造成多大影响。