¿Cuáles son las diferencias entre VirtualAlloc y HeapAlloc?

82

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?

Ajay
fuente

Respuestas:

83

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.

VirtualAlloc

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 VirtualFreepara desasignar.

HeapAlloc

Asigna el tamaño de memoria que solicite, no en grandes cantidades de VirtualAlloc. HeapAllocsabe cuándo necesita llamar VirtualAllocy 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 HeapFreepara liberar la memoria que le devuelven.

malloc

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 freepara desasignar. mallocLlamadas de Visual C ++ HeapAlloc.

nuevo

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 deletepara desasignar (o delete[]para matrices). Visual Studio newllama 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 SysAllocStringque le pueden indicar que debe usar en circunstancias específicas.

Doug
fuente
18
Doug: VirtualAlloc no se limita estrictamente a asignaciones de 4kb, es el tamaño devuelto por GetSystemInfo (), SYSTEM_INFO :: dwAllocationGranularity. En realidad es RARAMENTE 4kb. En mi host, es 64k, sospecho que lo mismo para ti. 4 KB es la entrada de tamaño de página mínimo para las diversas tablas de descriptores en la ABI x86. 4KB es el tamaño más pequeño que se puede autorizar independientemente, R / W / X, sin embargo, no tiene importancia para VirtualAlloc. Si consulta la documentación de VirtualAlloc, también existe la opción LARGE_PAGES (consulte msdn.microsoft.com/en-us/library/aa366568(VS.85).aspx ).
RandomNickName42
¿Sabes por qué DirectShow usa VirtualAlloc para asignar búferes de memoria para medios en lugar de usar malloc?
Aviad Rozenhek
Aviad: DirectShow usa la asignación virtual para reservar memoria para que pueda pasar los indicadores necesarios para las optimizaciones de rendimiento, cosas como no paginable o para que pueda reservar páginas físicas que pueden mejorar el rendimiento si su hardware lo admite.
RandomNickName42
8
@ RandomNickName42: esto no es correcto. La dirección de inicio de la asignación siempre está alineada con la granularidad (64 KB), pero la longitud de la asignación se redondea al tamaño de la página (4 KB). En efecto, el espacio de direcciones está reservado en fragmentos de 64 KB, pero comprometido en fragmentos de 4 KB. La alineación de la dirección de inicio no suele ser tan interesante, por lo que desde la mayoría de los puntos de vista, VirtualAlloc funciona en fragmentos de 4 KB. Estos detalles solo se vuelven importantes si está tratando de hacer muchos VirtualAllocs pequeños (se quedará sin espacio de direcciones) o algo elegante (como asignaciones adyacentes pero separadas).
arx
Pensé que malloc / new / CRT se llamaba HeapAlloc una vez y luego usaba sus propios algoritmos para devolver bloques de memoria del bloque de memoria HeapAlloc.
paulm
29

VirtualAlloces 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

  • ser muy grande
  • necesita ser compartido,
  • debe estar alineado con un valor particular (razones de desempeño) o
  • la persona que llama no necesita usar toda esta memoria a la vez ...
  • etc ...

HeapAlloces esencialmente lo que mallocy newambos 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.

newy mallocson algo iguales, malloces esencialmente una llamada exacta a HeapAlloc( heap-id-default ); newsin 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 VirtualAllocy 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;). newen C ++, mallocpara C, VirtualAllocpara casos masivos o IPC.

*** Tenga en cuenta que las asignaciones de memoria grandes realizadas por en HeapAllocrealidad se envían VirtualAllocdespué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 VirtualAllocque ninguno de los que respondieron a esta pregunta ha discutido.

VirtualAllocEx 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).

RandomNickName42
fuente
29

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:

ingrese la descripción de la imagen aquí

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.

Administrador de memoria en modo kernel

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í.

VirtualAlloc / VirtualFree

Estas son las API de nivel más bajo disponibles en el modo de usuario . La VirtualAllocfunción básicamente invoca ZwAllocateVirtualMemory que a su vez hace una rápida llamada al sistema a ring0relegar 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 dwAllocationGranularitypará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 0x10000bytes 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, pAddressse alineará en el 0x10000límite de bytes. Y aunque solicitó solo 8 bytes, el bloque de memoria real que obtendrá será la totalidad page(o algo así como 4Kbytes. El tamaño de página exacto se devuelve en el dwPageSizeparámetro). Pero, además de eso, todo el bloque de memoria que abarcan 0x10000bytes (o 64Ken 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 VirtualAllocasignaciones 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 VirtualAllocincorrecto puede provocar una fragmentación grave de la memoria.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

En pocas palabras, las funciones del montón son básicamente un envoltorio para la VirtualAllocfunció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:

  • HeapCreatereserva un gran bloque de memoria virtual llamando VirtualAllocinternamente (o ZwAllocateVirtualMemorypara 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 HeapAllocy en HeapFreerealidad 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.

  • HeapDestroya su vez llamadas VirtualFreeque 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 VirtualAlloccuando 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 .

malloc / libre

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).

nuevo / eliminar

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 mallocpara C, también son los envoltorios de las heapfunciones. 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 VirtualAllocde compartir memoria entre procesos. VirtualAllocpor sí mismo no permite compartir su memoria reservada / asignada con otros procesos. Para eso, se necesita usar una CreateFileMappingAPI 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.

ahmd0
fuente
7

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.


fuente
4

VirtualAlloc===> sbrk()bajo UNIX

HeapAlloc====> malloc()bajo UNIX

Mrad Chems Eddine
fuente
Esto no responde a la pregunta.
Dan Bechard
2

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 HeapAllocpero 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).

No hay problema de heno
fuente