ความยาวสูงสุดที่เป็นไปได้ของสตริง. NET คืออะไร?


239

สตริงที่ยาวที่สุดที่สามารถสร้างได้ใน. NET คืออะไร? เอกสารสำหรับStringชั้นเรียนเงียบในคำถามนี้เท่าที่ฉันเห็นดังนั้นคำตอบที่เชื่อถือได้อาจต้องใช้ความรู้เกี่ยวกับ internals การเปลี่ยนแปลงสูงสุดในระบบ 64 บิตจะเป็นอย่างไร

[คำถามนี้ถูกถามถึงความอยากรู้มากกว่าการใช้งานจริง - ฉันไม่ต้องการสร้างรหัสใด ๆ ที่ใช้สตริงขนาดมหึมา!]

คำตอบ:


346

ขีด จำกัด ทางทฤษฎีอาจเป็น 2,147,483,647 แต่ขีด จำกัด ในทางปฏิบัตินั้นไม่ใกล้เคียง เนื่องจากไม่มีวัตถุเดียวในโปรแกรม. NET ที่อาจมีขนาดเกิน 2GB และชนิดสตริงใช้ UTF-16 (2 ไบต์ต่ออักขระแต่ละตัว) สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือ 1,073,741,823 แต่คุณไม่สามารถจัดสรรได้ บนเครื่อง 32- บิต

นี่เป็นหนึ่งในสถานการณ์ที่"ถ้าคุณต้องถามคุณอาจทำอะไรผิดพลาด"


8
นี่คือคำตอบที่ถูกต้อง คุณมีแนวโน้มที่จะมีหน่วยความจำไม่เพียงพอก่อนที่จะจัดสรรได้เพียงพอที่จะทำให้ความยาวของสตริงหมดลง ในการบูตครั้งใหม่คุณอาจสามารถดึงการจัดสรร 2GB (ที่มีอักขระ 1M) ตามที่กล่าวไว้ที่นี่ แต่นั่นคือทั้งหมด
Stephen Deken

4
สมมติว่า "ไม่มีวัตถุใดที่อาจเกิน 2Gb" การยืนยันของคุณนั้นถูกต้องนี่เป็นข้อ จำกัด ทางทฤษฎีและข้อปฏิบัติ - ข้อ จำกัด ของความยาวสตริงจะเป็นขนาดวัตถุทั้งหมดไม่ใช่ความจุของฟิลด์ความยาว
McKenzieG1

12
หากใครสนใจค่าที่แน่นอนบนเครื่อง 64 บิตของฉันมีความยาว 1,073,741,791 (1024 · 1024 · 1024 - 33) ตัวอักษร ดูเพิ่มเติมคำถามที่เกี่ยวข้องของฉันเกี่ยวกับขนาดสูงสุดที่แน่นอนของ byte[]
svick

4
ฉันคลั่งไคล้คำตอบที่มีคำอธิบายสั้น ๆ แต่มีความลึก
Mikayil Abdullayev

3
มีตัวเลือกในการอนุญาตให้. NET 4.5 (และรุ่นที่ใหม่กว่า) วัตถุมีขนาดใหญ่กว่า 2GB บนเครื่อง 64 บิต ตรวจสอบที่นี่
Anderson Matos

72

จากการทดลองทางวิทยาศาสตร์และความแม่นยำสูงของฉันมันยอดเยี่ยมในเครื่องของฉันก่อน 1,000,000,000 ตัวอักษร (ฉันยังคงใช้รหัสด้านล่างเพื่อรับตำแหน่งที่ดีขึ้น)

UPDATE: หลังจากผ่านไปสองสามชั่วโมงฉันก็ยอมแพ้ ผลลัพธ์สุดท้าย: สามารถมีขนาดใหญ่กว่า 100,000,000 ตัวอักษรได้มากSystem.OutOfMemoryExceptionที่ 1,000,000,000 ตัวอักษร

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
}

35
ใช้ค้นหา binary ที่นี่อาจจะช่วยให้คุณหาคำตอบนี้ได้เร็วมาก ...
มาริโอ

49

เนื่องจากLengthคุณสมบัติของSystem.Stringคือ an Int32ฉันจึงเดาว่าความยาวสูงสุดจะเป็น 2,147,483,647 ตัวอักษร ( Int32ขนาดสูงสุด) หากอนุญาตให้ใช้นานกว่าคุณจะไม่สามารถตรวจสอบความยาวได้เนื่องจากจะล้มเหลว


2
@ m.edmondson: ฉันไม่เชื่อจริง ๆ อาร์เรย์สำหรับกรณีที่มีLongLengthเป็นอย่างดีและกระแสการใช้งานlongเป็นระยะเวลา แม้ว่าจะเป็นคำตอบที่ถูกต้อง แต่ก็เป็นวิธีที่แม่นยำในการวัดนี้
Willem Van Onsem

1
แต่สองบิตแรกนั้นใช้สำหรับการระบุ ASCII / ไม่ใช่ ASCII ตามที่บทความนี้บอกดังนั้นจึงควรเป็น 2 ^ 30 = 1 073 741 824
Saito

28

สำหรับใครก็ตามที่มาถึงหัวข้อนี้ช้าฉันจะเห็นว่า "คุณอาจไม่ควรทำอย่างนั้น" อาจทำให้บางคนถามว่าพวกเขาควรทำอย่างไร ...

StringBuilderระดับมักจะเปลี่ยนง่าย พิจารณาหนึ่งในคลาสที่อิงกับสตรีมโดยเฉพาะอย่างยิ่งหากข้อมูลของคุณมาจากไฟล์

ปัญหาs += "stuff"คือมันต้องจัดสรรพื้นที่ใหม่ทั้งหมดเพื่อเก็บข้อมูลแล้วคัดลอกข้อมูลเก่าทั้งหมดไปรวมกับสิ่งใหม่ - แต่ละและวนซ้ำทุกครั้ง ดังนั้นการเพิ่มห้าไบต์ถึง 1,000,000 ด้วยs += "stuff"มีค่าใช้จ่ายสูงมาก หากสิ่งที่คุณต้องการคือการเขียนห้าไบต์ไปยังจุดสิ้นสุดและดำเนินการกับโปรแกรมของคุณคุณต้องเลือกคลาสที่ออกจากห้องเพื่อการเติบโต:

StringBuilder sb = new StringBuilder(5000);
for (; ; )
    {
        sb.Append("stuff");
    }

StringBuilderจะเติบโตอัตโนมัติสองเท่าเมื่อถึงขีด จำกัด ดังนั้นคุณจะเห็นความเจ็บปวดจากการเติบโตเมื่อเริ่มต้นครั้งเดียวที่ 5,000 ไบต์อีกครั้งที่ 10,000 อีกครั้งที่ 20,000 การต่อสายอักขระจะทำให้เกิดความเจ็บปวดทุกครั้งที่วนซ้ำ


4
นอกจากนี้ยังเป็นที่น่าสังเกตว่า StringBuilder ช่วยให้คุณสามารถกำหนดขนาดเริ่มต้น มีประโยชน์ถ้าคุณรู้ว่าคุณกำลังจะใช้ 10,000 รายการก่อนเวลาช่วยให้คุณสามารถละเว้นการกระทืบบางส่วน
Kyle Baran

3
+1 สำหรับการมองผ่านคำถามและการตอบสนองต่อการออกแบบที่ดี เปรียบเทียบ "นี่คือสิ่งที่สตริงของคุณสามารถก่อนที่มันจะระเบิด" เมื่อเทียบกับ "ถ้าคุณต้องการเก็บข้อความจำนวนมากใช้สิ่งนี้ ... "
StevoInco

8

ความยาวสูงสุดของสตริงในเครื่องของฉันคือ1073741791

คุณเห็นไหมว่าสตริงไม่ได้ถูก จำกัด ด้วยจำนวนเต็มตามที่เชื่อกันโดยทั่วไป

ข้อ จำกัด ของหน่วยความจำนอกเหนือ Strings ต้องมีอักขระไม่เกิน 2 30 ( 1,073,741,824 ) ตัวเนื่องจากมีการกำหนดขีด จำกัด 2GB โดย Microsoft CLR (Common Language Runtime) มากกว่า 33 คอมพิวเตอร์ของฉันอนุญาต

ตอนนี้คุณสามารถลองด้วยตัวเองได้ที่นี่

สร้างแอปคอนโซล C # ใหม่ใน Visual Studio จากนั้นคัดลอก / วางวิธีหลักที่นี่:

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 เวลา 08/05/2019 12:06

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มคือ 100000000

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มขึ้นคือ 10,000,000 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

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มขึ้นคือ 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

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มขึ้นคือ 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

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มคือ 10,000 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

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มคือ 1,000 s.Length = 1073741000 ที่ 08/05/2019 12:06

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:06 หลังจากการทำลายล้างค่าของการเพิ่มขึ้นคือ 100 s.Length = 1073741100 ที่ 08/05/2019 12:06 s.Length = 1073741200 ที่ 08/05/2019 12:06 s.Length = 1073741300 เวลา 08/05/2019 12 : 07 s.Length = 1073741400 เวลา 08/05/2019 12:07 s.Length = 1073741500 เวลา 08/05/2019 12:07 s.Length = 1073741600 เวลา 08/05/2019 12:07 s.Length = 1073741700 เวลา 08/05/2019 12:07 น

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:07 น. หลังจากการทำลายล้างค่าของการเพิ่มขึ้นคือ 10 s.Length = 1073741710 ที่ 08/05/2019 12:07 s.Length = 1073741720 ที่ 08/05/2019 12:07 s.Length = 1073741730 เวลา 08/05/2019 12 : 07 s.Length = 1073741740 at 08/05/2019 12:07 s.Length = 1073741750 ที่ 08/05/2019 12:07 s.Length = 1073741760 ที่ 08/05/2019 12:07 s.Length = 1073741770 ที่ 08/05/2019 12:07 s.Length = 1073741780 at 08/05/2019 12:07 s.Length = 1073741790 เวลา 08/05/2019 12:07

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:07 น. หลังจากการทำลายล้างค่าของการเพิ่มคือ 1 s.Length = 1073741791 ที่ 08/05/2019 12:07

ข้อยกเว้นประเภท 'System.OutOfMemoryException' ถูกส่งออกไป เมื่อ 08/05/2019 12:07 น. หลังจากการทำลายล้างค่าของการเพิ่มคือ 0 การทดสอบเสร็จสมบูรณ์

ความยาวสูงสุดของสตริงคือ 1073741791

กดปุ่มใดก็ได้เพื่อดำเนินการต่อ

ความยาวสูงสุดของสตริงบนเครื่องของฉันคือ 1073741791

ฉันขอบคุณมากหากผู้คนสามารถโพสต์ผลลัพธ์ของพวกเขาเป็นความคิดเห็นด้านล่าง

มันจะน่าสนใจที่จะเรียนรู้ว่าผู้คนได้รับผลลัพธ์ที่เหมือนหรือต่างกัน


คุณเห็นไหมว่าสตริงไม่ได้ถูก จำกัด ด้วยจำนวนเต็มตามที่เชื่อกันโดยทั่วไป -> จำนวนเต็มใน c # สามารถเพิ่มได้ถึง 2,147,483,647 และผลลัพธ์ของคุณอยู่ใกล้มาก (น้อยกว่า 32 ไบต์) ถึงค่านี้หารด้วยสองซึ่งเป็นตรรกะตามตัวอักษรทุกตัวของ String จะถูกจัดเก็บเป็น Unicode ในสองไบต์ ดังนั้นแม้ว่าขีด จำกัด จะไม่ถูกกำหนดโดยขนาดของจำนวนเต็มมันก็ใกล้เคียงกับมันมาก
เบ็น

2

200 megs ... ที่จุดที่แอปของคุณหยุดทำงานเสมือนมีหน่วยความจำที่ตั้งค่าได้เป็นกิ๊กและ o / s เริ่มทำหน้าที่เหมือนคุณจะต้องรีบูท

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

5
ฉันไม่แน่ใจว่าพฤติกรรมที่คุณจะได้รับจากการสร้างสตริงที่ยิ่งใหญ่เพียงเส้นเดียวนั้นเหมือนกับสิ่งที่คุณเห็นโดยการจัดสรรกลุ่มของพวกเขาและเชื่อมโยงกัน
Casey

2

เนื่องจากString.Lengthเป็นจำนวนเต็ม (นั่นคือนามแฝงInt32) ขนาดของมันจึงถูก จำกัด ไว้ที่Int32.MaxValueตัวอักษร unicode ;-)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.