.NET字符串的最大可能长度是多少?

276

在.NET中能够创建的最长字符串是多少?就我所见,String类的文档对此问题并没有提供明确的答案,因此权威答案可能需要一些内部知识。在64位系统上,最大值是否会发生变化?

[这更多是出于好奇而不是实际使用 - 我没有打算创建任何使用巨大字符串的代码!]

8个回答

399

理论上的限制可能达到2,147,483,647,但实际限制远远没有那么高。由于.NET程序中没有单个对象可以超过2GB,并且字符串类型使用UTF-16(每个字符2个字节),因此您最多可以达到1,073,741,823个字符,但在32位机器上很难分配。

这是其中之一“如果您必须问,那么您可能正在做错什么”的情况。


8
这是正确答案。在能够分配足够长度来耗尽字符串之前,您很可能会耗尽内存。在刚启动时,您可能能够分配2GB(带有1M个字符),如此提到的那样,但仅限于这些情况。 - Stephen Deken
4
如果您的“没有单个对象可超过2GB”的说法是准确的,那么这既是理论上的限制也是实际上的限制 - 字符串长度的限制将是总对象大小,而不是长度字段的容量。 - McKenzieG1
17
如果有人对确切的值感兴趣,我在我的64位计算机上测试得到的是1,073,741,791(1024·1024·1024-33)个字符。请参阅我关于byte[]最大大小的相关问题 - svick
7
我疯狂地追求那些简短但深入的解释。 - Mikayil Abdullayev
5
64位计算机上有一种选项,可以允许.NET 4.5(及更高版本)对象的大小超过2GB。在这里查看 - Anderson Matos
显示剩余3条评论

77

根据我的高度科学和准确的实验,在我的机器上,它在1,000,000,000个字符之前就达到了极限。(我仍在运行以下代码以获得更好的定位)。

更新: 几个小时后,我放弃了。最终结果:可以处理比100,000,000个字符更多,但在1,000,000,000个字符处立即出现 System.OutOfMemoryException

using System;
using System.Collections.Generic;

public class MyClass
{
    public static void Main()
    {
        int i = 100000000;
        try
        {
            for (i = i; i <= int.MaxValue; i += 5000)
            {
                string value = new string('x', i);
                //WL(i);
            }
        }
        catch (Exception exc)
        {
            WL(i);
            WL(exc);
        }
        WL(i);
        RL();
    }

    #region Helper methods

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);   
    }

    private static void RL()
    {
        Console.ReadLine(); 
    }

    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

40
在这里应用二分查找可能会帮助你更快地找到这个答案。 - Mario

52
自从System.StringLength属性是一个Int32后,我猜最大长度应该是2,147,483,647个字符(最大Int32大小)。如果它允许更长的话,你不能检查长度,因为那样会失败。

2
@m.edmondson:实际上我并不认同。对于实例,数组也有“LongLength”,而流使用“long”作为长度。虽然这是一个有效的答案,但它并不是衡量这个问题的准确方式。 - Willem Van Onsem
1
但是根据这篇文章所说,前两位用于ASCII /非ASCII指示,因此应该是2^30 = 1,073,741,824。 - Saito

35
如果有人在晚期加入这个话题,我可以看到hitscan的“你可能不应该那样做”可能会导致有人问他们应该做什么... StringBuilder类通常是一个简单的替代品。特别是,如果您的数据来自文件,请考虑其中一个基于流的类s += "stuff"的问题在于它必须分配一个完全新的区域来保存数据,然后将所有旧数据以及新数据复制到它上面 - 每次循环迭代都要这样做。因此,使用s += "stuff"将五个字节添加到1,000,000个字节中非常昂贵。如果您想要的只是在末尾写入五个字节并继续进行程序,则必须选择一个留有一些增长空间的类:
StringBuilder sb = new StringBuilder(5000);
for (; ; )
    {
        sb.Append("stuff");
    }

StringBuilder 在达到其限制时会进行自动倍增。因此,您将在开始时看到一次增长痛苦,然后在5,000个字节处再次出现,然后在10,000和20,000处再次出现。每次循环迭代附加字符串都会产生这种痛苦。


6
值得注意的是,StringBuilder 还允许你设置初始大小。如果你预先知道要使用 1000 万条数据,这将非常有用,因为它可以忽略一些繁重的操作。 - Kyle Baran
4
感谢您看穿问题并提供好的设计解答。相比之下,“这是字符串达到崩溃点的大小”,与“如果你真的需要存储大量的文本,请使用这个...”不同。 - StevoInco
对于许多企业用例,例如需要处理且超过2GB的导出或日志,根据文档,具有最大值可达Int32(2147483647)的StringBuilder并不是解决方案。相同的限制仍然存在。 - Daniel ZA

14

在我的机器上,字符串的最大长度是1,073,741,791

你看,与普遍认为的不同,字符串并不受整数的限制。

除了内存限制外,由于Microsoft CLR(公共语言运行时)实施了2GB限制,因此字符串不能超过2301,073,741,824)个字符。比我的计算机少33个字符。

现在,你可以尝试一下自己做些什么。

在Visual Studio中创建一个新的C#控制台应用程序,然后将主方法复制/粘贴到这里:

static void Main(string[] args)
{
    Console.WriteLine("String test, by Nicholas John Joseph Taylor");

    Console.WriteLine("\nTheoretically, C# should support a string of int.MaxValue, but we run out of memory before then.");

    Console.WriteLine("\nThis is a quickish test to narrow down results to find the max supported length of a string.");

    Console.WriteLine("\nThe test starts ...now:\n");

    int Length = 0;

    string s = "";

    int Increment = 1000000000; // We know that s string with the length of 1000000000 causes an out of memory exception.

    LoopPoint:

    // Make a string appendage the length of the value of Increment

    StringBuilder StringAppendage = new StringBuilder();

    for (int CharacterPosition = 0; CharacterPosition < Increment; CharacterPosition++)
    {
        StringAppendage.Append("0");

    }

    // Repeatedly append string appendage until an out of memory exception is thrown.

    try
    {
        if (Increment > 0)
            while (Length < int.MaxValue)
            {
                Length += Increment;

                s += StringAppendage.ToString(); // Append string appendage the length of the value of Increment

                Console.WriteLine("s.Length = " + s.Length + " at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm"));

            }

    }
    catch (OutOfMemoryException ex) // Note: Any other exception will crash the program.
    {
        Console.WriteLine("\n" + ex.Message + " at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm") + ".");

        Length -= Increment;

        Increment /= 10;

        Console.WriteLine("After decimation, the value of Increment is " + Increment + ".");

    }
    catch (Exception ex2)
    {
        Console.WriteLine("\n" + ex2.Message + " at " + DateTime.Now.ToString("dd/MM/yyyy HH:mm") + ".");

        Console.WriteLine("Press a key to continue...");

        Console.ReadKey();

    }

    if (Increment > 0)
    {
        goto LoopPoint;

    }

    Console.WriteLine("Test complete.");

    Console.WriteLine("\nThe max length of a string is " + s.Length + ".");

    Console.WriteLine("\nPress any key to continue.");

    Console.ReadKey();

}

我的测试结果如下:

由尼古拉斯·约翰·约瑟夫·泰勒进行的字符串测试

理论上,C#应该支持长度为int.MaxValue的字符串,但是在那之前我们就会耗尽内存。

这是一个快速的测试,以缩小结果范围,找到字符串的最大支持长度。

测试现在开始:

s.Length = 1000000000 at 08/05/2019 12:06

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为100000000。

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为10000000。s.Length = 1010000000,时间为08/05/2019 12:06,s.Length = 1020000000,时间为08/05/2019 12:06,s.Length = 1030000000,时间为08/05/2019 12:06,s.Length = 1040000000,时间为08/05/2019 12:06,s.Length = 1050000000,时间为08/05/2019 12:06,s.Length = 1060000000,时间为08/05/2019 12:06,s.Length = 1070000000,时间为08/05/2019 12:06

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为1000000。s.Length = 1071000000,时间为08/05/2019 12:06,s.Length = 1072000000,时间为08/05/2019 12:06,s.Length = 1073000000,时间为08/05/2019 12:06

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为100000。s.Length = 1073100000,时间为08/05/2019 12:06,s.Length = 1073200000,时间为08/05/2019 12:06,s.Length = 1073300000,时间为08/05/2019 12:06,s.Length = 1073400000,时间为08/05/2019 12:06,s.Length = 1073500000,时间为08/05/2019 12:06,s.Length = 1073600000,时间为08/05/2019 12:06,s.Length = 1073700000,时间为08/05/2019 12:06

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为10000。s.Length = 1073710000,时间为08/05/2019 12:06,s.Length = 1073720000,时间为08/05/2019 12:06,s.Length = 1073730000,时间为08/05/2019 12:06,s.Length = 1073740000,时间为08/05/2019 12:06

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为1000。s.Length = 1073741000,时间为08/05/2019 12:06

在08/05/2019 12:06抛出了类型为“System.OutOfMemoryException”的异常。经过裁减,增量值为100。s.Length = 1073741100,时间为08/05/2019 12:06,


你看,字符串并不像普遍认为的那样受整数限制。在C#中,整数可以达到2,147,483,647,而你的结果非常接近(比这个值除以二少32字节),这是合理的,因为字符串的每个字符都以Unicode编码存储在两个字节中。因此,即使限制并不是由整数大小所施加的,它仍然非常接近。 - Ben
2
感谢您的代码。我已经在搭载ARM M1的OSX上成功复现了它,使用的是.NET 3.1版本。字符串的最大长度为1073741791。 - Paulo Henrique Lellis Gonalves

2

由于String.Length是一个整数(它是Int32的别名),因此它的大小限制为Int32.MaxValue个Unicode字符。;-)


0

200兆字节...在这一点上,您的应用程序会变得非常缓慢,占用大约1GB的工作集内存,操作系统开始表现得好像您需要重新启动。

static void Main(string[] args)
{
    string s = "hello world";
    for(;;)
    {
        s = s + s.Substring(0, s.Length/10);
        Console.WriteLine(s.Length);
    }
}

12
13
14
15
16
17
18
...
158905664
174796230
192275853
211503438

8
我不确定创建一个非常大的字符串所得到的行为是否与分配一堆字符串并连接它们所看到的行为相同。 - Casey

-1

字符串在RAM的堆中动态分配内存大小。但是字符串地址存储在占用4个字节内存的栈中。


这取决于引用的大小。在32位计算机上,为4个字节。在64位计算机上,则为8个字节。 - rbwhitaker

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