Jay*_*Jay 6 c# memory memory-management
我试图了解内存分配以及如何减少它们。
我创建了以下长列表进行测试
var list = new List<int>(Enumerable.Range(0, 1_000_000).ToArray());
Run Code Online (Sandbox Code Playgroud)
然后我循环它并打印一个像这样的字符串
for (var i = 0; i < list.Count; i++)
{
Console.WriteLine("Item # " + list[i]);
}
Run Code Online (Sandbox Code Playgroud)
上一个循环生成了超过 200 万次分配。我相信该行"Item # " + list[i]产生了 200 万个分配。
**问题1**为什么在这种情况下会分配200万个字符串?是否list[i]必须作为字符串存储在堆中,这是 1 次分配,然后是另一个分配中的组合字符串,因此每个循环 2 次分配?
下一步我考虑使用字符串生成器来减少分配
var builder = new StringBuilder();
for (var i = 0; i < list.Count; i++)
{
builder.Append("Item # ");
builder.Append(list[i]);
Console.WriteLine(builder.ToString());
builder.Clear();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码有 100 万次分配,是之前分配的一半。
但是,仍然有很多分配。
我知道字符串是 C# 中的不可变对象,每个字符串在堆中都有自己的分配。但是,有没有一种方法可以重用内存分配,以便我们可以创建 1 个字符串,然后在该循环内一遍又一遍地重用相同的分配?就我而言,一旦打印了字符串,我就不再需要它了。对我来说,重用相同的分配并更新它的值是安全的。
**问题2** 是否可以重复使用内存分配来减少分配量?
** 问题 3** 我还可以尝试哪些其他技巧来改进我的循环?
是否可以重复使用内存分配来减少分配量?
是的。将前缀“Item #”放入单独的变量中,并创建 aSpan<char>来存储 的字符串表示形式list[i]。Span每次获得新号码时,您都可以重复使用该号码。
var prefix = "Item # ";
char[] arr = new char[6];
Span<char> number = new(arr);
for (int i = 0 ; i < list.Count ; i++) {
Console.Write(prefix);
list[i].TryFormat(number, out int charsWritten);
Console.Out.WriteLine(number[0..charsWritten]);
}
Run Code Online (Sandbox Code Playgroud)
笔记:
Span一个char[]. 请注意,数组应该足够大以存储“999999”,即要打印的最长数字(就字符串表示形式而言)。TryFormat写入跨度并告诉我们它写入了多少个字符。我们利用这些信息,以便我们只使用WriteLine它写入的字符,而不再使用。TryFormat返回一个bool指示整数是否成功写入跨度的值。如果这是您担心的事情,请考虑检查一下。Console.Out才能使用WriteLine需要跨度的重载。Console本身没有这样的静态方法。StringBuilder您可以通过使用与内部类似的技巧(它使用Span's 和ISpanFormattable接口来减少分配)并使用流来完全删除打印循环的分配Console.Out:
// allocate big enough buffer to hold the largest formatted number:
char[] buffer = new char[8];
var span = buffer.AsSpan();
var allocatedBefore = GC.GetTotalAllocatedBytes(); // to check allocations
for (var i = 0; i < list.Count; i++)
{
// write prefix, due to interning it won't be allocated every time
// in other cases can be moved to outside scope as variable/parameter
Console.Write("Item # ");
// Note - returns bool, in general case might need to check
// can be false if the span is not big enough
list[i].TryFormat(span, out int written);
// write formatted line to stdout via bufffer
Console.Out.WriteLine(buffer, 0, written);
}
// allocations checks
var allocatedAfter = GC.GetTotalAllocatedBytes();
Console.WriteLine(allocatedAfter - allocatedBefore);
Run Code Online (Sandbox Code Playgroud)
阅读更多:
| 归档时间: |
|
| 查看次数: |
346 次 |
| 最近记录: |