C# 非托管内存中的可变字符串在托管空间中可用

C# 非托管内存中的可变字符串在托管空间中可用,c#,.net,string,pointers,unmanaged-memory,C#,.net,String,Pointers,Unmanaged Memory,注意:我的案例是在一个只处理字符串的旧API生态系统中,没有现代的.NET添加。 所以我非常需要一个没有分配的可变字符串。字符串每X毫秒更新一次,这样您就可以在几分钟内计算出它可以产生多少垃圾(StringBuilder在这里根本不相关)。我目前的方法是预先分配固定大小的字符串,并通过钉住、直接写入字符和在达到容量时自动脱落或抛出来对其进行变异 这个很好用。分配的字符串是长期存在的,因此最终GC会将其升级到Gen2,并且固定不会太麻烦它,从而最小化开销。但有两个主要问题: 因为字符串是固定的,所

注意:我的案例是在一个只处理字符串的旧API生态系统中,没有现代的.NET添加。

所以我非常需要一个没有分配的可变字符串。字符串每X毫秒更新一次,这样您就可以在几分钟内计算出它可以产生多少垃圾(StringBuilder在这里根本不相关)。我目前的方法是预先分配固定大小的字符串,并通过钉住、直接写入字符和在达到容量时自动脱落或抛出来对其进行变异

这个很好用。分配的字符串是长期存在的,因此最终GC会将其升级到Gen2,并且固定不会太麻烦它,从而最小化开销。但有两个主要问题:

  • 因为字符串是固定的,所以我必须用
    \0
    填充它,虽然到目前为止,这在所有默认的网络/单声道功能和第三方的东西上都运行得很好,但是当字符串的长度为1024时,没有办法知道其他东西会有什么反应,但最后100个是
    \0
  • 我不能调整它的大小,因为这将导致分配。我可以一个月分配一次,但由于字符串是相当动态的,我不能确定它何时会尝试进一步扩展或收缩。我可以使用“仅扩展”方法,这样我只在需要扩展时分配,但是,这有填充开销的缺点(如果字符串扩展为5k个字符,但下一个字符串仅为3k-2k个字符将被填充以获得额外的周期)以及内存额外使用。我不确定GC对McGigage会有什么感觉,McGigage在Gen2中经常是固定字符串,而不是LOH中另一种方法是池可重用字符串对象,但是,这具有更高的内存和GC开销+查找开销
  • 由于目标字符串必须存在相当长的一段时间,我正在考虑通过字节缓冲区将其移动到。这将消除GC的负担(钉住惩罚开销),并且我可以以比托管堆更低的成本重新调整大小/重新分配。 我很难理解的是-我如何才能分割分配的非托管缓冲区的特定部分,并将其包装为正常的网络字符串,以便在托管空间/代码中使用?比如,将它传递到控制台。WriteLine或某个第三方库,该库在屏幕上绘制UI标签并接受字符串。这是否可行

    另外,据我所知,NET5的计划(我认为将在NET6中最终确定)将不再能够改变字符串之类的东西(运行时阻塞或未定义的故障)。他们的解决方案似乎是POH,这基本上就是我所描述的,具有相同的局限性

    我如何可能分割分配的非托管缓冲区的特定部分,并将其包装为普通的网络字符串,以便在托管空间/代码中使用

    据我所知这是不可能的。Net有自己的定义对象(对象头等)的方法,您不能将某些任意内存区域视为.Net对象。固定和变异字符串似乎很危险,因为字符串是不可变的,有些事情可能无法正常工作(例如,将字符串用作字典键)

    正确的方法是(正如Canton7所提到的)使用
    char[]
    缓冲区和
    Span
    /
    内存对字符串进行切片。当传递给其他方法时,可以将字符串的一部分转换为实际的字符串对象。当调用诸如
    Console.WriteLine
    或UI方法之类的方法时,分配字符串对象的开销与正在进行的其他事情相比是无关紧要的

    如果您有只接受
    string
    的旧代码,您可能需要接受它所带来的限制,或者重写代码以接受内存/span表示


    我强烈建议进行分析,看看这是否是频繁分配的实际问题。只要字符串适合小对象堆(SOH,即小于87kb),并且没有升级到gen 2,开销可能不会很大。SOH上的分配速度很快,运行第0代GC的时间不会直接与分配的数量成比例。因此,每隔几毫秒更新一次可能并不可怕。如果你说的是微秒,我会更担心。

    “GC会将它升级到Gen2,钉扎不会太麻烦它,从而最小化开销。”——这不一定是真的。如果字符串在gen0中被固定,那么GC可能会决定根本不升级它!如果它被推广,它仍然会阻碍压实。如果它是在gen2中被固定的,你可能是对的。我真的鼓励你不要走变异字符串的路线——有很多事情假设字符串不会变异。你不能用字符数组吗。NET字符串以长度开头,因此不能将字符数组的任意位分割成字符串,因为它没有相应的头。如果可以,我会在内存中保留一个经过变异的
    char[]
    。当您需要将其转换为字符串以传递到
    控制台.WriteLine
    等(我假设您不是每X ms这么做!)时,请使用
    string.Create
    char[]
    的相关部分复制到新字符串中。不,我有一个只接受字符串的旧API/生态系统。如果我有能力使用字符数组或跨度,这个问题就不存在了。你提到的所有这些建议我已经说过很多次了,它们在这个具体案例中根本不相关。是的,但是因为你没有提到它们,人们会建议它们。我不是一个读心术的人!