Hay un montón de método para asignar memoria en el entorno de Windows, tales como VirtualAlloc
, HeapAlloc
, malloc
, new
.
Entonces, ¿cuál es la diferencia entre ellos?
Respuestas:
Cada API es para diferentes usos. Cada uno también requiere que use la función de desasignación / liberación correcta cuando haya terminado con la memoria.
Una API de Windows de bajo nivel que ofrece muchas opciones, pero que es principalmente útil para personas en situaciones bastante específicas. Solo se puede asignar memoria en (editar: no 4 KB) trozos más grandes. Hay situaciones en las que lo necesita, pero sabrá cuando se encuentre en una de estas situaciones. Uno de los más comunes es si tiene que compartir la memoria directamente con otro proceso. No lo use para la asignación de memoria de uso general. Úselo VirtualFree
para desasignar.
Asigna el tamaño de memoria que solicite, no en grandes cantidades de VirtualAlloc
. HeapAlloc
sabe cuándo necesita llamar VirtualAlloc
y lo hace automáticamente. Me gusta malloc
, pero es solo para Windows y ofrece un par de opciones más. Adecuado para asignar fragmentos generales de memoria. Algunas API de Windows pueden requerir que use esto para asignar la memoria que les pasa, o usar su complemento HeapFree
para liberar la memoria que le devuelven.
La forma C de asignar memoria. Prefiera esto si está escribiendo en C en lugar de C ++, y desea que su código funcione, por ejemplo, en computadoras Unix también, o si alguien dice específicamente que necesita usarlo. No inicializa la memoria. Adecuado para asignar fragmentos generales de memoria, como HeapAlloc
. Una API simple. Úselo free
para desasignar. malloc
Llamadas de Visual C ++ HeapAlloc
.
La forma de C ++ de asignar memoria. Prefiera esto si está escribiendo en C ++. También coloca un objeto u objetos en la memoria asignada. Úselo delete
para desasignar (o delete[]
para matrices). Visual Studio new
llama HeapAlloc
, y luego tal vez inicialice los objetos, dependiendo de cómo lo llame.
En los estándares recientes de C ++ (C ++ 11 y superior), si tiene que usarlo manualmente delete
, lo está haciendo mal y debería usar un puntero inteligente como unique_ptr
. Desde C ++ 14 en adelante, se puede decir lo mismo de new
(reemplazado con funciones como make_unique()
).
También hay un par de otras funciones similares como las SysAllocString
que le pueden indicar que debe usar en circunstancias específicas.
VirtualAlloc
es una asignación especializada del sistema de memoria virtual (VM) del SO. Las asignaciones en el sistema VM deben realizarse con una granularidad de asignación que (la granularidad de la asignación) depende de la arquitectura. La asignación en el sistema VM es una de las formas más básicas de asignación de memoria. Las asignaciones de VM pueden tomar varias formas, la memoria no está necesariamente dedicada o respaldada físicamente en la RAM (aunque puede serlo). La asignación de VM suele ser un tipo de asignación de propósito especial , ya sea porque la asignación debe
HeapAlloc
es esencialmente lo que malloc
y new
ambos eventualmente llaman. Está diseñado para ser muy rápido y utilizable en muchos tipos diferentes de escenarios de una asignación de propósito general. Es el "montón" en un sentido clásico. Los montones son en realidad configurados por a VirtualAlloc
, que es lo que se usa para reservar inicialmente espacio de asignación del sistema operativo. Después de que el espacio es inicializado por VirtualAlloc
, se configuran varias tablas, listas y otras estructuras de datos para mantener y controlar el funcionamiento del HEAP. Parte de esa operación consiste en dimensionar dinámicamente (aumentar y reducir) el montón, adaptar el montón a usos particulares (asignaciones frecuentes de algún tamaño), etc.
new
y malloc
son algo iguales, malloc
es esencialmente una llamada exacta a HeapAlloc( heap-id-default )
; new
sin embargo, puede [adicionalmente] configurar la memoria asignada para objetos C ++ . Para un objeto dado, C ++ almacenará vtables en el montón para cada llamador. Estos vtables son redireccionamientos para ejecución y forman parte de lo que le da a C ++ sus características OO como herencia, sobrecarga de funciones, etc.
Algunos otros métodos de asignación comunes como _alloca()
y _malloca()
están basados en pilas ; Las asignaciones de archivos se asignan realmente VirtualAlloc
y se establecen con indicadores de bits particulares que designan esas asignaciones como de tipo FILE
.
La mayoría de las veces, debe asignar la memoria de una manera que sea consistente con el uso de esa memoria;). new
en C ++, malloc
para C, VirtualAlloc
para casos masivos o IPC.
*** Tenga en cuenta que las asignaciones de memoria grandes realizadas por en HeapAlloc
realidad se envían VirtualAlloc
después de cierto tamaño (un par de cientos de k o 16 MB o algo que olvido, pero bastante grande :)).
*** EDITAR Comenté brevemente sobre IPC y VirtualAlloc
, también hay algo muy bueno sobre un relacionado VirtualAlloc
que ninguno de los que respondieron a esta pregunta ha discutido.
VirtualAlloc
Ex es lo que un proceso puede usar para asignar memoria en un espacio de direcciones de un proceso diferente . Por lo general, esto se usa en combinación para obtener la ejecución remota en el contexto de otro proceso a través de CreateRemoteThread (similar a CreateThread
, el hilo simplemente se ejecuta en el otro proceso).
Es muy importante comprender la distinción entre las API de asignación de memoria (en Windows) si planea usar un lenguaje que requiere administración de memoria (como C o C ++). Y la mejor manera de ilustrarlo en mi humilde opinión es con un diagrama:
Tenga en cuenta que esta es una vista específica de Windows muy simplificada.
La forma de entender este diagrama es que cuanto más alto en el diagrama está un método de asignación de memoria, mayor nivel de implementación utiliza. Pero comencemos desde abajo.
Proporciona todas las reservas de memoria y las asignaciones para el sistema operativo, así como soporte para archivos mapeados en memoria , memoria compartida , copia-en-escritura operaciones, etc. No es accesible directamente desde el código en modo de usuario, por lo que voy a omitir aquí.
Estas son las API de nivel más bajo disponibles en el modo de usuario . La VirtualAlloc
función básicamente invoca ZwAllocateVirtualMemory que a su vez hace una rápida llamada al sistema a ring0
relegar esa forma al gestor de la memoria del núcleo. También es el método más rápido para reservar / asignar un bloque de memoria nueva de todos los disponibles en el modo de usuario.
Pero viene con dos condiciones principales:
Solo asigna bloques de memoria alineados en el límite de granularidad del sistema.
Solo asigna bloques de memoria del tamaño que es el múltiplo de la granularidad del sistema.
Entonces, ¿cuál es la granularidad de este sistema ? Puede obtenerlo llamando a GetSystemInfo . Se devuelve como dwAllocationGranularity
parámetro. Su valor es específico de la implementación (y posiblemente del hardware), pero en muchos sistemas Windows de 64 bits se establece en 0x10000
bytes o 64K
.
Entonces, lo que todo esto significa es que si intenta asignar, diga solo un bloque de memoria de 8 bytes con VirtualAlloc
:
void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
Si tiene éxito, pAddress
se alineará en el 0x10000
límite de bytes. Y aunque solicitó solo 8 bytes, el bloque de memoria real que obtendrá será la totalidad page
(o algo así como 4K
bytes. El tamaño de página exacto se devuelve en el dwPageSize
parámetro). Pero, además de eso, todo el bloque de memoria que abarcan 0x10000
bytes (o 64K
en la mayoría de los casos) desde pAddress
no estarán disponibles para otras asignaciones. Entonces, en cierto sentido, al asignar 8 bytes, también podría estar solicitando 65536.
Entonces, la moraleja de la historia aquí no es sustituir las VirtualAlloc
asignaciones de memoria genéricas en su aplicación. Debe usarse para casos muy específicos, como se hace con el montón a continuación. (Por lo general, para reservar / asignar grandes bloques de memoria).
El uso VirtualAlloc
incorrecto puede provocar una fragmentación grave de la memoria.
En pocas palabras, las funciones del montón son básicamente un envoltorio para la VirtualAlloc
función. Otras respuestas aquí brindan un concepto bastante bueno. Agregaré que, en una vista muy simplista, la forma en que funciona el montón es la siguiente:
HeapCreate
reserva un gran bloque de memoria virtual llamando VirtualAlloc
internamente (o ZwAllocateVirtualMemory
para ser específico). También configura una estructura de datos interna que puede rastrear asignaciones de tamaño más pequeño dentro del bloque reservado de memoria virtual.
Cualquier llamada a HeapAlloc
y en HeapFree
realidad no asigna / libera ninguna memoria nueva (a menos que, por supuesto, la solicitud exceda lo que ya se ha reservado HeapCreate
), sino que miden (o commit
) una porción grande previamente reservada, al diseccionarla en bloques de memoria más pequeños que solicita un usuario.
HeapDestroy
a su vez llamadas VirtualFree
que en realidad liberan la memoria virtual.
Entonces, todo esto hace que las funciones de montón sean candidatos perfectos para asignaciones de memoria genérica en su aplicación. Es ideal para asignaciones de memoria de tamaño arbitrario. Pero un pequeño precio a pagar por la conveniencia de las funciones del montón es que introducen una ligera sobrecarga VirtualAlloc
cuando se reservan bloques de memoria más grandes.
Otra cosa buena del montón es que realmente no es necesario crear uno. Generalmente se crea para usted cuando comienza su proceso. Entonces uno puede acceder llamando a la función GetProcessHeap .
Es un contenedor específico del idioma para las funciones del montón . A diferencia de HeapAlloc
, HeapFree
, etc. estas funciones van a trabajar no sólo si su código es compilado para Windows, sino también para otros sistemas operativos (como Linux, etc.)
Esta es una forma recomendada de asignar / liberar memoria si programa en C. (a menos que esté codificando un controlador de dispositivo en modo kernel específico).
Ven como un operador de administración de memoria de alto nivel (bueno, para C++
). Son específicos para el C++
lenguaje y, al igual que malloc
para C
, también son los envoltorios de las heap
funciones. También tienen un montón de su propio código que se ocupa de la C++
inicialización específica de constructores, la desasignación de destructores, la generación de una excepción, etc.
Estas funciones son una forma recomendada de asignar / liberar memoria y objetos si programa en C++
.
Por último, quiero hacer un comentario sobre lo que se ha dicho en otras respuestas sobre el uso VirtualAlloc
de compartir memoria entre procesos. VirtualAlloc
por sí mismo no permite compartir su memoria reservada / asignada con otros procesos. Para eso, se necesita usar una CreateFileMapping
API que pueda crear un bloque de memoria virtual con nombre que se pueda compartir con otros procesos. También puede asignar un archivo en el disco a la memoria virtual para acceso de lectura / escritura. Pero ese es otro tema.
En resumen:
VirtualAlloc, HeapAlloc, etc. son API de Windows que asignan memoria de varios tipos desde el sistema operativo directamente. VirtualAlloc administra páginas en el sistema de memoria virtual de Windows, mientras que HeapAlloc asigna desde un montón de SO específico. Francamente, es poco probable que necesite utilizar alguno de ellos.
malloc es una función de biblioteca estándar C (y C ++) que asigna memoria a su proceso. Las implementaciones de malloc normalmente usarán una de las API del sistema operativo para crear un grupo de memoria cuando se inicie la aplicación y luego asignarla a medida que realiza solicitudes de malloc
new es un operador C ++ estándar que asigna memoria y luego llama a los constructores de manera apropiada en esa memoria. Puede implementarse en términos de malloc o en términos de las API del sistema operativo, en cuyo caso también creará un grupo de memoria en el inicio de la aplicación.
VirtualAlloc
===> sbrk()
bajo UNIX
HeapAlloc
====> malloc()
bajo UNIX
VirtualAlloc
=> Se asigna directamente a la memoria virtual, usted reserva / confirma en bloques. Esto es ideal para asignaciones grandes, por ejemplo, matrices grandes.
HeapAlloc
/ new
=> asigna la memoria en el montón predeterminado (o cualquier otro montón que pueda crear). Esto se asigna por objeto y es ideal para objetos más pequeños. El montón predeterminado es serializable, por lo tanto, tiene una asignación de subprocesos garantizada (esto puede causar algunos problemas en escenarios de alto rendimiento y es por eso que puede crear sus propios montones).
malloc
=> usa el montón de tiempo de ejecución de C, similar HeapAlloc
pero es común para escenarios de compatibilidad.
En pocas palabras, el montón es solo una parte de la memoria virtual que está gobernada por un administrador de montón (en lugar de memoria virtual sin procesar)
El último modelo en el mundo de la memoria son los archivos mapeados en memoria, este escenario es ideal para grandes cantidades de datos (como archivos grandes). Esto se usa internamente cuando abre un EXE (no carga el EXE en la memoria, solo crea un archivo mapeado en la memoria).