2012-02-29 17 views
10

Tengo curiosidad por saber cómo se representan las referencias de objeto C# en la memoria en tiempo de ejecución (en .NET CLR). Algunas preguntas que vienen a la mente son:¿cómo se representan las referencias de objeto C# en la memoria/en tiempo de ejecución (en el CLR)?

  1. ¿Cuánta memoria ocupa una referencia de objeto? ¿Difiere cuando se define en el alcance de una clase frente al alcance de un método? ¿En dónde vive difiere según este alcance (stack vs heap)?

  2. ¿Cuál es la información actual mantenida dentro de una referencia de objeto? ¿Es simplemente una dirección de memoria que apunta al objeto al que se refiere o hay más que eso? ¿Esto difiere dependiendo de si está definido dentro del alcance de una clase o método?

  3. Mismas preguntas que las anteriores, pero esta vez cuando se habla de una referencia a una referencia, como cuando una referencia de objeto se pasa a un método por referencia. ¿Cómo cambian las respuestas a 1 y 2?

+3

Tenga en cuenta que estas preguntas son todos los detalles de implementación (que están sujetos a cambios) y no realmente sobre C#, sino más bien sobre .NET CLR. – dlev

+0

Chopperdave, buena pregunta interesante, pero quiero preguntar si estás preguntando qué querías decir: una referencia de objeto es básicamente un puntero y eso es solo un 'número' según la arquitectura del sistema en el que se ejecuta el código. Si está preguntando cómo funcionan las asignaciones de .Net Heap, esa es una bestia completamente diferente. –

+0

Solo quería agregar, no hay insulto pretendido aquí, no estoy tratando de dar a entender que usted no sabe lo que quiere decir - El asunto es, es que en .Net esta es una pregunta ambigua y ayudará a los futuros usuarios de Stack Desbordamiento para saber exactamente de qué contexto estamos hablando. –

Respuesta

12

Esta respuesta se entiende más fácilmente si comprende los punteros C/C++. Un puntero es simplemente la dirección de memoria de algunos datos.

  1. una referencia de objeto debe ser el tamaño de un puntero, que normalmente es de 4 bytes en una CPU de 32 bits, y 8 bytes en una CPU de 64 bits. Es lo mismo independientemente de donde se define. Donde vive depende de donde se define. Si es un campo de una clase, residirá en el montón en el objeto del que forma parte. Si se trata de un campo estático, se encuentra en una sección especial del montón que no está sujeta a la recolección de basura. Si es una variable local, vive en la pila.

  2. Una referencia de objeto es simplemente un puntero, que se puede visualizar como un int o long que contiene la dirección del objeto en la memoria. Es lo mismo independientemente de donde se define.

  3. Esto se implementa como un puntero a un puntero. Los datos son los mismos, solo una dirección de memoria. Sin embargo, no hay ningún objeto en la dirección de memoria dada. En cambio, hay otra dirección de memoria, que es la referencia original al objeto. Esto es lo que permite modificar un parámetro de referencia. Normalmente, un parámetro desaparece cuando se completa su método. Como la referencia al objeto no es un parámetro, los cambios a esta referencia se mantendrán. La referencia a una referencia desaparecerá, pero no la referencia. Este es el propósito para pasar los parámetros de referencia.

Una cosa que usted debe saber, los tipos de valores se almacenan en su lugar (no hay una dirección de memoria, sino que se almacenan directamente en donde sería la dirección de memoria - Ver # 1). Cuando se pasan a un método, se realiza una copia y esa copia se utiliza en el método. Cuando se pasan por referencia, se pasa una dirección de memoria que ubica el tipo de valor en la memoria, lo que permite que se modifique.

Editar: Como dlev señaló, estas respuestas no son la regla más difícil, ya que no hay una regla que diga que así es como debe ser. .NET es libre de implementar estas preguntas como lo desee. Sin embargo, esta es la forma más probable de implementarlo, ya que así es como funciona la CPU de Intel internamente, por lo que usar cualquier otro método probablemente sería ineficiente.

Espero no haberlo confundido demasiado, pero no dude en preguntar si necesita alguna aclaración.

+0

Los tipos de valores no siempre se almacenan en la pila. –

+2

"los tipos de valores se almacenan en la pila": eso no es cierto.Los tipos de valor ** pueden ** almacenarse en la pila, pero no siempre es así. P.ej. un campo de tipo de valor en un tipo de referencia se almacena en el montón, en el objeto al que pertenece. +1 de todos modos, porque su respuesta es en su mayoría correcta ... –

+0

@ThomasLevesque: Se eliminó la referencia a los tipos de valores en la pila. ¡Gracias! –

13

.NET Heaps and Stacks Este es un tratamiento exhaustivo de cómo funcionan la pila y el montón.

C# y otros lenguajes de POO montón utilizan en el uso general de referencia jerga no trata los punteros de referencias en este contexto (C# también es capaz de usar punteros!) Analogías de puntero funcionan para algunos conceptos generales, pero esto conceptual modelo se descompone para preguntas como esta. Consulte la excelente publicación de Eric Lippert sobre este tema Handles are Not Addresses

No es apropiado decir que una Manija es del tamaño de un puntero. (aunque puede ser coincidentemente el mismo) Los identificadores son alias para objetos, no es necesario que sean una dirección formal de un objeto.

En este caso, el CLR pasa a utilizar las direcciones reales de los mangos: Desde el enlace de arriba:

... el CLR hace realmente poner en práctica las referencias de objetos gestionados como direcciones de objetos propiedad del recolector de basura , pero ese es un detalle de implementación .

Así que sí un mango es probablemente de 4 bytes en una arquitectura de 32 bits, y 8 bytes en una arquitectura de 64 bytes, pero esto no es un "seguro", y es no por el hecho de punteros. Vale la pena señalar que, según la implementación del compilador y , los rangos de direcciones utilizados algunos tipos de punteros pueden ser diferentes en tamaño.

Con todo este contexto, probablemente pueda modelar esto por analogía con un puntero, pero es importante tener en cuenta que los identificadores no requieren direcciones. El CLR podría elegir cambiar esto si quisiera en el futuro y los consumidores del CLR no deberían saber nada mejor.

Una última unidad de este punto sutil:

Este es un C# Puntero:

int* myVariable; 

Este es un C# Mango:

object myVariable; 

Ellos no son los mismos.

Puede hacer cosas como las matemáticas en los punteros, que no debe hacer con las asas. Si su identificador se implementa como un puntero y lo usa como si fuera un puntero, usted está haciendo un mal uso del Mango de alguna manera que podría ocasionarle problemas más adelante.

+3

No es un puntero C# porque es ilegal hacer un puntero a un tipo de referencia administrado. 'int *' sería un ejemplo de un tipo de puntero C#. –

Cuestiones relacionadas