2010-06-24 23 views
7

Hoy noté que la clase String de C# devuelve la longitud de una cadena como Int. Dado que un Int es siempre de 32 bits, sin importar la arquitectura, ¿significa que una cadena solo puede tener 2 GB o menos de longitud?¿Las cadenas de C# (y otras API de .NET) están limitadas a 2 GB de tamaño?

Una secuencia de 2 GB sería muy inusual, y presenta muchos problemas junto con ella. Sin embargo, la mayoría de las API .NET parecen usar 'int' para transmitir valores como la longitud y el recuento. ¿Esto significa que siempre estamos limitados a tamaños de colección que caben en 32 bits?

Parece un problema fundamental con las API de .NET. Hubiera esperado que cosas como el recuento y la longitud se devolvieran a través del equivalente de 'size_t'.

+21

Si mi respuesta fue una cadena de 2 GB, podría echar otro vistazo al problema. –

+0

Es una broma, ¿verdad? – arthurprs

+2

Nitpick: dado que .NET codifica caracteres con UTF-16, asignando (al menos) dos bytes para cada carácter, una cadena de longitud máxima tendría 2^31 caracteres y consumiría al menos ** 4 GB ** o memoria, no * * 2GB **. –

Respuesta

5

Correcto, la longitud máxima sería el tamaño de Int32, sin embargo, es probable que se encuentre con otros problemas de memoria si se trata de cadenas más grandes que eso de todos modos.

+0

Esto se aplica a más de cadena aunque. Se aplica a la mayoría de todas las colecciones. – Andrew

+1

@Andrew: la respuesta también cubre esa afirmación. Si tiene una colección que se acerca a los 2 GB, también tendrá otros problemas. –

+0

Supongamos que es el año 2060 y estoy trabajando en una aplicación en mi PC ultramoderna que requiere colecciones con más de una cantidad total de elementos. ¿Qué problemas podría tener? – Andrew

15

parece un problema fundamental con la API .NET ...

No sé si me gustaría ir tan lejos.

Considere casi cualquier clase de colección en .NET. Es probable que tenga una propiedad Count que devuelva int. Así que esto sugiere que la clase está limitada a un tamaño de int.MaxValue (2147483647). Eso no es realmente un problema ; es una limitación - y una perfectamente razonable, en la gran mayoría de los escenarios.

De todos modos, ¿cuál sería la alternativa? Hay uint, pero eso no es compatible con CLS. Luego está long ...

¿Qué pasa si Length devolvió long?

  1. Se necesitarían 32 bits adicionales de memoria en cualquier lugar donde se desee conocer la longitud de una cadena.
  2. El beneficio sería: podríamos tener cadenas que ocupan miles de millones de gigabytes de RAM. Hurra.

tratar de imaginar el costo alucinante de algún código como este:

// Lord knows how many characters 
string ulysses = GetUlyssesText(); 

// allocate an entirely new string of roughly equivalent size 
string schmulysses = ulysses.Replace("Ulysses", "Schmulysses"); 

Básicamente, si usted está pensando en string como una estructura de datos destinada a almacenar una cantidad ilimitada de texto, Tengo expectativas poco realistas. Cuando se trata de objetos de este tamaño, es cuestionable si tiene alguna necesidad de mantenerlos en la memoria (a diferencia del disco duro).

+3

No veo cómo es razonable. Dado que .NET define un int para ser de 32 bits, eso significa 50 años a partir de ahora ... no importa lo que mi computadora pueda manejar, .NET me restringirá a las colecciones de tamaño de 32 bits. Parece que una variación moderna de '640 Kb es suficiente para cualquiera'. – Andrew

+5

@Andrew, en 50 años, no estarás programando en .NET. Y en 50 años, int.MaxValue todavía sería una gran cantidad de objetos para contener en una colección. –

+2

@Andrew luego crea un contenedor alrededor de un 'List <>/Array' multidimensional y lo devuelve' Int64' para 'Count' – Earlz

3

En algún valor de String.length() probablemente alrededor de 5MB no es realmente práctico usar String nunca más. String está optimizado para pequeños fragmentos de texto.

Piense en lo que sucede cuando

msString += " more chars" 

Algo así como:

sistema calcula la longitud de miCadena más la longitud de los "más caracteres"

sistema asigna esa cantidad de memoria

El sistema copia myString a la nueva ubicación de memoria

El sistema copia "más caracteres" a la nueva ubicación de la memoria después de la última copia de myString char

El myString original queda a merced del recolector de elementos no utilizados.

Si bien esto es agradable y ordenado para pequeños trozos de texto, es una pesadilla para cadenas grandes, solo encontrar 2 GB de memoria contigua es probablemente un inconveniente.

Así que si sabe que está manejando más de unos pocos MB de caracteres, use una de las * clases de almacenamiento intermedio.

+1

Incluso las clases de buffer devuelven un int para cosas como length. – Andrew

0

Incluso en las versiones x64 de Windows, fui golpeado por .Net que limitaba cada objeto a 2GB.

2GB es bastante pequeño para una imagen médica. 2GB es incluso pequeño para una imagen de descarga de Visual Studio.

+1

Esta es mi preocupación. Parece que la mayoría de las API's .NET proporcionan un int para cosas como 'count' o 'length'. – Andrew

+1

@Michael - No me importan demasiado las cuerdas en particular, fue solo un ejemplo para atraer la atención de las personas. – Andrew

+1

Parece que alguien tuvo ese problema con 'Array' desde el principio, ya que tiene una propiedad' LongLength' de 64 bits. – devstuff

1

Es bastante improbable que necesite almacenar más de dos mil millones de objetos en una sola colección. Vas a incurrir en penalizaciones de rendimiento bastante serias al hacer enumeraciones y búsquedas, que son los dos propósitos principales de las colecciones. Si está tratando con un conjunto de datos tan grande, es casi seguro que puede tomar otra ruta, como dividir su colección individual en muchas colecciones más pequeñas que contienen partes del conjunto completo de datos con los que está trabajando.

Heeeey, espera un segundo .... ya tenemos este concepto - se llama diccionario!

Si necesita almacenar, por ejemplo, 5 mil millones de cadenas en inglés, utilizar este tipo:

Dictionary<string, List<string>> bigStringContainer; 

Hagamos de la cadena de la clave representan, por ejemplo, los dos primeros caracteres de la cadena. A continuación, escribir un método de extensión de esta manera:

public static string BigStringIndex(this string s) 
{ 
    return String.Concat(s[0], s[1]); 
} 

y luego añadir elementos a bigStringContainer así:

bigStringContainer[item.BigStringIndex()].Add(item); 

y lo llaman un día. (Obviamente hay maneras más eficientes de hacerlo, pero esto es solo un ejemplo)

Ah, y si realmente realmente necesitas poder buscar cualquier objeto arbitrario por índice absoluto, usa un Array en su lugar de una colección. De acuerdo, sí, utilizas algún tipo de seguridad, pero puedes indexar elementos de la matriz con un long.

+0

Incluso si pudieras indexar en una matriz con un 'largo ', sería bastante inútil: la CLR tiene un límite máximo de 2GB, por lo que es imposible que una matriz tenga más elementos que' 'int.MaxValue' de todos modos (y solo podría acercarse a ese límite si fuera una matriz 'bool []' o 'byte []' con elementos de un solo byte). * Esta restricción se aplica a la implementación actual de Microsoft, no estoy seguro acerca de Mono. * – LukeH

-1

Si está trabajando con un archivo de 2GB, eso significa que probablemente va a utilizar mucha RAM y verá un rendimiento muy lento.

En su lugar, para archivos muy grandes, considere el uso de un MemoryMappedFile (consulte: http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles.memorymappedfile.aspx). Usando este método, puede trabajar con un archivo de tamaño casi ilimitado, sin tener que cargar todo en la memoria.

+0

Por favor ponga un comentario si marca una respuesta. ¿De qué manera esto no fue útil o correcto, me pregunto? –

1

El hecho de que el marco utiliza Int32 para Count/Length propiedades, indexadores, etc., es un poco una pista falsa. El problema real es que CLR actualmente tiene una restricción de tamaño de objeto máximo de 2 GB.

Así que un string - o cualquier otro objeto único - nunca puede ser mayor que 2GB.

cambio de la propiedad Length del tipo string para volver long, ulong o incluso BigInteger sería inútil, ya que nunca podría tener más de aproximadamente 2^30 caracteres de todos modos (tamaño de 2 GB máximo y 2 bytes por carácter.)

De manera similar, debido al límite de 2 GB, las únicas matrices que podrían aproximarse a tener 2^31 elementos serían bool[] o byte[] matrices que solo usan 1 byte por elemento.

Por supuesto, no hay nada que le impida crear sus propios tipos de compuestos para resolver la restricción de 2 GB.

(Tenga en cuenta que las observaciones anteriores se aplican a la aplicación actual de Microsoft, y muy bien podría cambiar en futuras versiones. No estoy seguro de si Mono tiene límites similares.)

+0

¿Tiene alguna referencia para esto? – Russell

+0

@Russell: "Al igual que con los sistemas operativos Windows de 32 bits, hay un límite de 2 GB en el tamaño de un objeto que puede crear mientras ejecuta una aplicación administrada de 64 bits en un sistema operativo Windows de 64 bits". http://msdn.microsoft.com/en-us/library/ms241064.aspx – LukeH

+1

@Russell: También hay un artículo de blog interesante aquí, con un ejemplo de un objeto compuesto de solución alternativa: http://blogs.msdn.com/ b/joshwil/archive/2005/08/10/450202.aspx – LukeH

1

En las versiones de .NET antes de 4.5, el tamaño máximo del objeto es 2GB. A partir de 4.5, puede asignar objetos más grandes si gcAllowVeryLargeObjects está habilitado. Tenga en cuenta que el límite para string no se ve afectado, pero las "matrices" también deberían abarcar "listas", ya que las listas están respaldadas por matrices.

Cuestiones relacionadas