2009-08-23 11 views
8

GetMem le permite asignar un búfer de tamaño arbitrario. En algún lugar, el administrador de la memoria retiene la información de tamaño, ya que no es necesario que le diga qué tan grande es el búfer cuando pasa el puntero a FreeMem.¿Cómo puedo encontrar el tamaño de la memoria a la que hace referencia un puntero?

¿Es esa información solo para uso interno, o hay alguna forma de recuperar el tamaño del búfer al que apunta un puntero?

+0

relacionado: http://stackoverflow.com/questions/969007/is-it-possible-to-get-the-size-of-the-type-that-a-pointer-points-to-in-delphi -7/http://stackoverflow.com/questions/232691/how-can-i-get-the-size-of-an-array-from-a-pointer-in-c –

Respuesta

8

Parecería que el tamaño de un bloque referenciado por un puntero devuelto por GetMem() debe estar disponible desde algún lugar, dado que FreeMem() no requiere que identifique el tamaño de la memoria que se liberará: el sistema debe ser capaz de determinarlo, ¿por qué no el desarrollador de la aplicación?

Pero, como han dicho otros, los detalles precisos de la gestión de la memoria implicada NO están definidos por el sistema per se ....Delphi siempre ha tenido una arquitectura de administrador de memoria reemplazable, y la "interfaz" definida para los administradores de memoria compatibles no requiere que proporcionen esta información para un puntero arbitrario.

El administrador de memoria predeterminado mantendrá la información necesaria de la forma que se adapte a él, pero algún otro administrador de memoria utilizará un mecanismo totalmente diferente, aunque superficialmente similar, incluso si piratea una solución basada en el conocimiento íntimo de un administrador de memoria, si cambia el administrador de memoria (o si se cambia para usted, por ejemplo, por un cambio en el sistema definido, administrador de memoria que tal vez esté usando por defecto, como ocurrió entre Delphi 2005 y 2006, por ejemplo), entonces su la solución casi con certeza se romperá.

En general, no es una suposición razonable por parte del gestor de memoria/RTL que la aplicación ya debe saber el tamaño de un trozo de memoria que un GetMem() puntero asignado refiere, dado que la aplicación pidió en primer lugar! :)

Y si su aplicación NO asignó el puntero entonces el administrador de memoria de su aplicación no tiene manera de saber qué tan grande puede ser el bloque al que hace referencia. Puede ser un puntero al medio de un bloque más grande, por ejemplo, ¡solo la fuente del puntero puede saber cómo se relaciona con la memoria a la que hace referencia!

Pero, si la aplicación realmente necesita para mantener dicha información al respecto es propios punteros, entonces podría, por supuesto, diseñar fácilmente un medio para lograr esto con una clase Singleton simple o biblioteca de funciones a través del cual GetMem()/FreeMem() las solicitudes se enrutan para mantener un registro del tamaño solicitado asociado para cada puntero asignado actualmente. Tal mecanismo podría, por supuesto, exponer fácilmente esta información según sea necesario, de manera totalmente confiable e independiente de cualquier administrador de memoria que esté en uso.

Esto puede ser la única opción si se requiere un registro "preciso", ya que una implementación determinada del administrador de memoria puede asignar un bloque de memoria mayor para un tamaño de datos determinado que el solicitado. No sé si algún administrador de memoria lo hace, pero podría hacerlo en teoría, por razones de eficiencia.

0

¿Es esa información solo para uso interno, o hay alguna forma de recuperar el tamaño del búfer al que apunta un puntero?

¿Estas dos "alternativas" se contradicen entre sí?

Solo para uso interno.

+0

Lo que quise decir es, es que información oculta del desarrollador porque es solo para uso interno, ¿o hay alguna forma de llegar a ella? –

+0

Lo que * I * significaba que, incluso si la información es para uso interno, aún puede tener una forma de obtenerla. O puede que no. La afirmación verdadera es que * esta información es solo para uso interno *. Todo lo demás no importa. –

0

Hay cierta información antes del área asignada para almacenar metainformación. Esto significa que, cada vez que asigna una pieza de memoria, se asigna una pieza más grande y los primeros bytes se usan para metainformación. El puntero devuelto está en el bloque que sigue a esta metainformación.

Me imagino que el formato se cambia con otra versión del administrador de memoria, así que no cuente con esto.

0

Esa información es solo para uso interno.

Tenga en cuenta que los administradores de memoria no necesitan almacenar el tamaño como parte de la memoria devuelta, muchos administradores de memoria lo almacenarán en una tabla interna y usarán la dirección de memoria del inicio dado como una clave de búsqueda en esa mesa

5

Es para uso interno, ya que depende del MemoryManager utilizado. Por cierto, es por eso que necesitas usar el par GetMem/FreeMem del mismo MemoryManager; no hay una manera canónica de saber cómo se ha reservado la memoria.
En Delphi, si nos fijamos en FastMM4, se puede ver que la memoria se asigna en pequeñas, medianas o grandes bloques:
los pequeños bloques se asignan de piscinas de bloques de tamaño fijo (tamaño de bloque se define a la piscina nivel en el tipo de bloque)

TSmallBlockType = packed record 
    {True = Block type is locked} 
    BlockTypeLocked: Boolean; 
    {Bitmap indicating which of the first 8 medium block groups contain blocks 
    of a suitable size for a block pool.} 
    AllowedGroupsForBlockPoolBitmap: byte; 
    {The block size for this block type} 
    BlockSize: Word; 

los bloques medianas también están asignados en piscinas, pero que tienen un tamaño variable

{Medium block layout: 
    Offset: -8 = Previous Block Size (only if the previous block is free) 
    Offset: -4 = This block size and flags 
    Offset: 0 = User data/Previous Free Block (if this block is free) 
    Offset: 4 = Next Free Block (if this block is free) 
    Offset: BlockSize - 8 = Size of this block (if this block is free) 
    Offset: BlockSize - 4 = Size of the next block and flags 

    {Get the block header} 
    LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^; 
    {Get the medium block size} 
    LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask; 

la grandes bloques se asignan individualmente con el tamaño requerido

TLargeBlockHeader = packed record 
    {Points to the previous and next large blocks. This circular linked 
    list is used to track memory leaks on program shutdown.} 
    PreviousLargeBlockHeader: PLargeBlockHeader; 
    NextLargeBlockHeader: PLargeBlockHeader; 
    {The user allocated size of the Large block} 
    UserAllocatedSize: Cardinal; 
    {The size of this block plus the flags} 
    BlockSizeAndFlags: Cardinal; 
    end; 
+0

Pensé que ansistring dependía de que el tamaño estuviera prefijado. Parece que no hay una manera uniforme de cargar el tamaño de bloque para hacer esto, ¿cómo funciona esto? ¿Hay un tamaño en la asignación ahora? –

+1

AnsiString tiene el prefijo * long *, pero eso se debe a que el RTL agrega 4 bytes adicionales antes de llamar a 'GetMem'. (Más otros 4 para el recuento de referencias y, a partir de Delphi 2009, 2 para la página de códigos y 2 para el tamaño del elemento). Cuando alarga una cadena, el RTL llama a 'ReallocMem', lo que permite al administrador de memoria elegir si para inflar la asignación in situ o para asignar un nuevo bloque (con una nueva dirección). Ha sido así para siempre; AnsiStrings no usa la contabilidad interna del administrador de memoria. –

Cuestiones relacionadas