我有一個應用程序,我將日誌字符串保留在循環緩衝區中。當日志滿了時,對於每一個新的插入,舊的字符串將被釋放以進行垃圾收集,然後它們在第二代內存中。因此,最終會發生第二代GC,這是我想避免的。如何避免長生存的字符串導致第2代垃圾回收
我試圖將字符串編組爲一個結構。令人驚訝的是,我仍然獲得第二代GC:s。看來這個結構仍然保留對字符串的一些引用。下面完整的控制檯應用任何幫助讚賞。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
[StructLayout(LayoutKind.Sequential)]
public struct FixedString
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
private string str;
public FixedString(string str)
{
this.str = str;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct UTF8PackedString
{
private int length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
private byte[] str;
public UTF8PackedString(int length)
{
this.length = length;
str = new byte[length];
}
public static implicit operator UTF8PackedString(string str)
{
var obj = new UTF8PackedString(Encoding.UTF8.GetByteCount(str));
var bytes = Encoding.UTF8.GetBytes(str);
Array.Copy(bytes, obj.str, obj.length);
return obj;
}
}
const int BufferSize = 1000000;
const int LoopCount = 10000000;
static void Main(string[] args)
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
"Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)");
Console.WriteLine();
for (int i = 0; i < 5; i++)
{
TestPerformance<string>(s => s);
TestPerformance<FixedString>(s => new FixedString(s));
TestPerformance<UTF8PackedString>(s => s);
Console.WriteLine();
}
Console.ReadKey();
}
private static void TestPerformance<T>(Func<string, T> func)
{
var buffer = new T[BufferSize];
GC.Collect(2);
Stopwatch stopWatch = new Stopwatch();
var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
stopWatch.Reset();
stopWatch.Start();
for (int i = 0; i < LoopCount; i++)
buffer[i % BufferSize] = func(i.ToString());
stopWatch.Stop();
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
typeof(T).Name.PadRight(20),
stopWatch.ElapsedMilliseconds,
(GC.CollectionCount(0) - initialCollectionCounts[0]),
(GC.CollectionCount(1) - initialCollectionCounts[1]),
(GC.CollectionCount(2) - initialCollectionCounts[2])
);
}
}
}
編輯:更新的代碼UnsafeFixedString,做必要的工作:在我的電腦上
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication
{
class Program
{
public unsafe struct UnsafeFixedString
{
private int length;
private fixed char str[256];
public UnsafeFixedString(int length)
{
this.length = length;
}
public static implicit operator UnsafeFixedString(string str)
{
var obj = new UnsafeFixedString(str.Length);
for (int i = 0; i < str.Length; i++)
obj.str[i] = str[i];
return obj;
}
}
const int BufferSize = 1000000;
const int LoopCount = 10000000;
static void Main(string[] args)
{
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
"Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)");
Console.WriteLine();
for (int i = 0; i < 5; i++)
{
TestPerformance(s => s);
TestPerformance<UnsafeFixedString>(s => s);
Console.WriteLine();
}
Console.ReadKey();
}
private static void TestPerformance<T>(Func<string, T> func)
{
var buffer = new T[BufferSize];
GC.Collect(2);
Stopwatch stopWatch = new Stopwatch();
var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
stopWatch.Reset();
stopWatch.Start();
for (int i = 0; i < LoopCount; i++)
buffer[i % BufferSize] = func(String.Format("{0}", i));
stopWatch.Stop();
Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
typeof(T).Name.PadRight(20),
stopWatch.ElapsedMilliseconds,
(GC.CollectionCount(0) - initialCollectionCounts[0]),
(GC.CollectionCount(1) - initialCollectionCounts[1]),
(GC.CollectionCount(2) - initialCollectionCounts[2])
);
}
}
}
輸出是:
Type Time GC(0) GC(1) GC(2)
String 5746 160 71 19
UnsafeFixedString 5345 418 0 0
爲什麼你想避免第2代垃圾回收? – PVitt
該應用程序有一些實時要求。 GC(2)導致完全凍結。 –
如果您將消息存儲爲byte [](encoding.utf8.getbytes()而不是字符串) –