2012-05-28 11 views
10

Necesitaría almacenar una cadena de código de idioma, como "en", que siempre contendrá 2 caracteres.¿Cómo se almacenan los tipos String y Char en la memoria en .NET?

¿Es mejor para definir el tipo como "Cadena" o "Char"?

private string languageCode; 

vs

private char[] languageCode; 

o hay otra, una mejor opción?

¿Cómo se guardan estos 2 en la memoria? ¿Cuántos bytes o bits se les asignarán cuando se asignen los valores?

+5

¿Ha hecho demostrado a sí mismo que este es un problema todavía? Raramente encontré la necesidad de preocuparme por la memoria cuando uso cuerdas, especialmente las más pequeñas. Si no se muestra como un problema, no te preocupes hasta que lo esté. Es una solución fácil en una fecha posterior si las cadenas están causando un problema de memoria. De lo contrario, use una cadena y ni siquiera piense en problemas de memoria. –

+0

Tengo una lógica muy intensa que almacena miles de estos en la memoria por lo que cada poco ayuda. –

+0

@William Si el rendimiento es tan crítico, ¿por qué no declarar un 'enum LanguageCode: short' y guardar 2 bytes? –

Respuesta

8

cómo se almacenan

Tanto el string y la char[] se almacenan en el montón - lo que el almacenamiento es el mismo. Internamente yo asumiría un string simplemente es una cubierta para char[] con mucha de código extra para que sea útil para usted.

Además, si tiene muchas cadenas repetitivas, puede utilizar Interning para reducir la huella de memoria de esas cadenas.

La mejor opción

yo estaría a favor de cadena - es inmediatamente más evidente lo que el tipo de datos es y cómo va a utilizarlo. La gente también está más acostumbrada a usar cuerdas para que el mantenimiento no sufra. También se beneficiará enormemente de todo el código repetitivo que se ha hecho por usted. Microsoft también se ha esforzado mucho para asegurarse de que el tipo string no sea un cerdo de rendimiento.

La asignación Tamaño

no tengo ni idea de cuánto se asigna, creo que las cadenas son muy eficientes en cuanto a que sólo se asignan suficiente para almacenar los caracteres Unicode - ya que son inmutables que es seguro hacerlo . Las matrices tampoco se pueden cambiar de tamaño sin asignar el espacio en una nueva matriz, así que supongo que solo toman lo que necesitan.

Overhead of a .NET array?

Alternativas

Con base en la información de que sólo hay 20 códigos de lenguaje y el rendimiento es clave, se puede declarar su propia enumeración con el fin de reducir el tamaño requerido para representar los códigos:

enum LanguageCode : byte 
{ 
    en = 0, 
} 

Esto sólo se llevará a 1 byte en oposición a 4+ para dos char (en una matriz), pero no limitar la gama de availabl e LanguageCode valores en el rango de byte - que es más que suficiente para 20 elementos.

Puede ver el tamaño de los tipos de valores utilizando el operador sizeof(): sizeof(LanguageCode). Los enumerados no son más que el tipo subyacente bajo el capó, por defecto son int, pero como puede ver en mi ejemplo de código, puede cambiar eso "heredando" un nuevo tipo.

+0

No interpone cadenas explícitamente en .Net; están internados para ti implícitamente por su mera declaración. Además, cadenas y matrices de carbonilla son extremadamente diferentes en .Net, dado que los arrays de char son estructuras mutables en el montón, o incluso la pila en función de cómo ellos declarados, mientras que las cadenas son inmutables y cuando el artículo se ha vinculado a las notas, construir y construir y acumular en el grupo interno en lugar de la memoria regular de .NET Framework, lo que significa que pueden ser un gran desperdicio. –

+0

@ChrisMoschini No todas las cadenas están internados. Los literales son internados, pero no hay mucho más. Si toma una cadena como entrada, la lee de un archivo de recursos u otra fuente, no se la interna. Tienes que internarlos manualmente. Curiosamente, mi respuesta ni siquiera decía de ninguna manera. –

+0

Eso depende de cómo el código está escrito - por ejemplo, si se trata de la búsqueda de un montón de trozos de cuerda declarados en el código, sin embargo, terminan con un montón de cuerdas internados. Pero la importante preocupación de rendimiento es tirar un montón de cadenas intermedias innecesarias en el montón cuando sabes que no las necesitas; una única matriz de caracteres siempre será mucho más económica en cuanto a la memoria, y si escribes un código similar al internos de Regex, más económico para CPU. Menos memoria utilizada en .Net significa menos GC también, lo que tiene otro beneficio para la CPU. –

0

Si desea almacenar exactamente 2 caracteres, y lo hace de manera más eficiente, utilice una estructura:

struct Char2 
{ 
public char C1, C2; 
} 

Utilizando esta estructura por lo general no causa nuevas asignaciones del montón. Simplemente convertirá en un objeto existente (en la cantidad mínima posible) o consumirá espacio en la pila, lo cual es muy barato.

+0

Las asignaciones de montón dependerán completamente de * donde * declaras la estructura. Solo estará en la pila si se declara dentro de los métodos/propiedades. Dentro de las clases estará en el montón, con el resto de los miembros de la clase. –

+0

No causará una * nueva * asignación. Simplemente convertirá en un objeto existente (en la cantidad mínima posible). – usr

+0

Sí cierto, pero las asignaciones de montón generalmente son muy rápidas y no deberían preocuparse inicialmente. Dicho esto, una estructura de 'struct LanguageCode' es una buena opción. –

4

Respuesta corta: Usar cadena

Respuesta larga:

private string languageCode; 

cadenas que yo sepa se almacenan como una matriz de longitud prefijada de caracteres. Un objeto String se crea una instancia en el montón para mantener esta matriz sin formato. Sin embargo, un objeto String es mucho más que un simple conjunto que permite realizar operaciones básicas de cuerda como la comparación, la concatenación, la subcadena extracción, buscar, etc

Mientras

private char[] languageCode; 

se almacena como una serie de caracteres, es decir, un objeto Array se creará en el montón y luego se usará para administrar tus personajes. Pero todavía tiene un atributo de longitud que se almacena internamente por lo que no hay ahorros aparentes en la memoria cuando se compara con una cadena. Aunque, presumiblemente, una matriz es más simple que una cadena y puede tener menos variables internas, ofreciendo una menor huella de memoria (esto debe verificarse).

Pero OTOH pierde la capacidad de realizar operaciones de cadena en esta matriz de caracteres. Incluso operaciones como la comparación de cadenas se vuelven engorrosas ahora. ¡Demasiado tiempo corto usa una cuerda!

1

¿Cómo se guardan estos 2 en la memoria? ¿Cuántos bytes o bits se les asignarán cuando se asignen los valores?

Cada ejemplo en .NET se almacena como sigue: un IntPtr campo -sized para el identificador de tipo; uno más para bloquear la instancia; el resto es datos de campo de instancia redondeados a un valor IntPtr. Por lo tanto, en una plataforma de 32 bits cada instancia ocupa 8 bytes + datos de campo.

Esto se aplica tanto a string como a char[]. Ambos también almacenan la longitud de los datos como un entero de tamaño IntPtr, seguido de los datos reales. Por lo tanto, un string de dos caracteres y un char[] de dos caracteres, en una plataforma de 32 bits, ocupará 8 + 4 + 4 = 16 bytes.

La única manera de reducir esto al almacenar exactamente dos caracteres es almacenar los caracteres reales, o una estructura que contenga los caracteres, en un campo o una matriz. Todos estos habría consumir sólo 4 bytes para los caracteres:

// Option 1 
class MyClass 
{ 
    char Char1, Char2; 
} 

// Option 2 
class MyClass 
{ 
    CharStruct chars; 
} 
... 
struct CharStruct { public char Char1; public char Char2; } 

MyClass acabará utilizando 8 bytes (en una máquina de 32 bits) por ejemplo, además de los 4 bytes para los caracteres.

// Option 3 
class MyClass 
{ 
    CharStruct[] chars; 
} 

Esto utilizará 8 bytes para la sobrecarga MyClass, además de 4 bytes para el charsreferencia, más 12 bytes para la sobrecarga de la matriz, además de 4 bytes por CharStruct de la matriz.

+0

Interesante. ¿De dónde sacaste esta información? – kristianp

+1

@kristianp Gran parte de esta información proviene de este artículo de MSDN: https://msdn.microsoft.com/en-us/magazine/cc163791.aspx (desplácese hacia abajo hasta la Figura 6) –

0

Cuerdas de hecho tienen una sobrecarga de tamaño de una longitud puntero, es decir, 4 bytes para un proceso de 32 bits, 8 bytes para un proceso de 64 bits. Pero, de nuevo, las cadenas ofrecen mucho más a cambio que las matrices de caracteres.

Si la aplicación utiliza muchas cadenas cortas y no necesitan utilizar sus propiedades y métodos de las cadenas que a menudo, probablemente podría seguras unos pocos bytes de memoria. Pero si desea usar cualquiera de ellos como una cadena, primero tendrá que crear una nueva instancia de cadena. No veo cómo esto te ayudará a tener memoria lo suficientemente segura como para valer la pena.

Cuestiones relacionadas