如何在.NET字符串密集应用程序中减少内存占用?

19

我有一个应用程序,它在内存中保存了约1,000,000个字符串出于性能考虑。我的应用程序消耗了约200 MB的 RAM。

我想减少字符串所占用的内存量。

我知道.NET使用UTF-16编码(每个字符2字节)表示字符串。我的应用程序中大部分字符串都是纯英文字符,因此将它们存储为UTF-8编码将比UTF-16更有效率。

是否有一种方法可以在内存中以UTF-8编码存储字符串,并允许使用标准字符串函数?(我的需求主要包括使用StringComparison.OrdinalIgnoreCase进行IndexOf操作)。


3
你必须将所有100万个字符串都加载到内存中吗?你能否提供更多关于你在内存中处理这些字符串的具体细节? - Dean Kuga
7
为什么200MB会成为问题?您是否遇到了低内存或内存耗尽的问题? - Lasse V. Karlsen
6
200MB是一个问题吗?您的用户可用内存很少吗?请注意,我并不是说200MB是可以接受的,这取决于应用程序,但通常情况下,当人们抱怨内存使用和应用程序时,他们没有考虑到他们拥有所有这些可用内存的一个原因; 让应用程序运行快! - Lasse V. Karlsen
2
我喜欢将内存使用比作车库空间的使用。如果你有一个可以容纳大约10辆车的大车库,为什么还要在角落里争论一个平方英尺的工作台呢? - Lasse V. Karlsen
1
@Lasse V. Karlsen♦ 我的用户包括但不限于所有家庭用户。 - DxCK
显示剩余18条评论
5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
13

很不幸,您无法更改 .Net 字符串的内部表示。我的猜测是 CLR 针对多字节字符串进行了优化。

您正在处理的是著名的时空权衡范例,它指出为了获得更多的内存,您必须使用更多的处理器,或者您可以通过使用一些内存来节省处理器。

话虽如此,请看一下这里here的一些考虑因素。如果我是您,一旦确定内存增益足够,尝试编写自己的“字符串”类,该类使用 ASCII 编码。这可能已经足够了。

更新:

关于钱的更多信息,您应该查看 StackOverflow 传奇人物 Jon Skeet 的这篇文章 "Of memory and strings",该文章涉及到您所面临的问题。对不起,我没有立即提到它,花了我一些时间才找到 Jon 的确切帖子。


4
有办法在内存中以UTF-8编码存储字符串并使用标准的字符串函数吗?(我的需求主要包括使用StringComparison.OrdinalIgnoreCase进行IndexOf)。您可以将其存储为字节数组,并提供自己的IndexOf实现(因为将其转换回字符串以进行IndexOf可能会带来很大的性能损失)。为此,请使用System.Text.Encoding函数(最好是进行构建步骤以将其转换为字节,然后从磁盘读取字节数组 - 仅在需要显示时将其转换回字符串)。您可以将它们存储在C / C ++库中,让您使用单字节字符串。您可能不想要将它们搬回来,但是您可能只需轻微地移植结果(我假设这里有某种搜索),而不会对性能产生太大影响。 C ++ / CLI可能会使此过程更加容易(通过能够在C ++ / CLI中编写搜索代码,但在C ++中编写字符串“数据库”)。或者,您可以重新审视初始性能问题,需要在内存中使用所有字符串。嵌入式数据库、索引等可能会加速您的事情,并减少内存使用量 - 并且更易于维护。

@DxCK,你遇到的问题是如果你限制自己只使用8位,那么你就无法支持世界上使用的大部分语言,即使使用C++和UTF8也是如此。 - Chris S
1
@Chris S UTF8编码如何限制语言? - DxCK
@DxCK - 这似乎是另一个 Stack Overflow 的问题。 ;) Boost 有一个字符串库,还有各种 Windows API 等。如果你选择使用字节数组,你需要自己提供,我想。我恐怕我的 Unicode 知识有限,无法解答 - 不过我想对于纯 ASCII 序列可以使用简单的 ASCII 比较方法,对于复杂的 Unicode 比较则可以利用 BCL。 - Mark Brackett
1
@sgorozco - 我认为你混淆了UTF-8(它是Unicode)和纯ASCII。UTF-8将ASCII字符存储为单个字节,但是可变宽度以存储Unicode的其余部分。 - Mark Brackett
@MarkBrackett 我知道,我想我试图向 DxCK 传达的观点是,除非你检查编码并执行你所提到的操作,否则你可能需要在某个时候返回一个 string,写自己的 indexof 函数。 - Chris S
显示剩余4条评论

2
如果您将其存储为bytearray,需要时只需恢复为字符串以执行一些操作。我会创建一个类来设置和获取字符串,该类在内部将其作为bytearray存储。 转换为bytearray:
string s = "whatever";
byte[] b = System.Text.Encoding.UTF8.GetBytes(s);

转换为字符串:

string s = System.Text.Encoding.UTF8.GetString(b);

1
我尝试过了。将其转换回字符串几乎没有性能成本:分配内存,从UTF-8转换为UTF-16,然后进行垃圾回收。对于1,000,000个字符串,这是非常明显的成本。 - DxCK
@DxCK "然后进行垃圾回收" - 你这样说是什么意思? - H H
你想要什么...更好的性能还是更小的存储占用? :) 你的应用程序是否需要一直保留每一个字符串?如果不是,也许只需要将那些已经很久没被使用的字符串存储下来。可以制作一个类来做一些内部“内存收集”,而不是进行垃圾回收。 - SpoBo
我猜字节数组不行,因为他需要搜索字符串。 - Chris S
如果您重写String类并使用首选的字符编码,那么您可以使用字节数组并获得良好的性能。记得使用数据结构。 - Patrick Lorio
当它是乌尔都语时会发生什么? - Chris S

2

尝试使用内存数据库作为“存储”并使用SQL与数据交互……例如,SQLite可以部署为应用程序的一部分(只包含1-2个DLL,可以放置在应用程序相同的文件夹中)…


0
如果你创建自己的UTF-8字符串类(UTF8String?),并提供对String的隐式转换,会怎样呢?为了节省内存,你可能会牺牲一些速度,但这可能正是你想要的。

我试过了。将其转换回字符串几乎没有性能成本。从UTF-8转换为UTF-16,然后进行垃圾回收。对于1,000,000个字符串,这是非常明显的成本。 - DxCK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,