Ahora que ya sabemos que los montones son propensos a fragmentarse, vamos a explicar una nueva característica que ha introducido Microsoft en Windows XP y Windows .NET Server: los montones de baja fragmentación (
low fragmentation heaps: LFH).
Los montones normales están optimizados para recibir muchas peticiones de bloques de tamaño pequeño. Sin embargo, si hacemos peticiones de bloques grandes, el rendimiento no será el mejor posible.
Los LFH son un tipo especial de montones, que se comportan de forma distinta a la hora de reservar memoria de su interior. ¿Recordáis cuando hemos dicho que es recomendable reservar siempre bloques de un tamaño que sea potencia de 2? Pues precisamente esa es la filosofía de estos montones.
Cuando se hace una petición de memoria a un LFH (con HeapAlloc, como ya sabemos), el sistema no hará la búsqueda en el
free chain como ya hemos explicado, sino que retorna un bloque de un tamaño dentro de una serie de tamaños prefijados.
Microsoft han definido 128 tamaños distintos, y siempre se retornará un bloque de alguno de esos tamaños, independientemente del espacio que pidamos con HeapAlloc o HeapReAlloc.
Con esta técnica, no evitamos que el montón esté fragmentado, aunque lo que sí conseguimos es que los trozos sean, como máximo, de 128 tamaños distintos. ¿Y para qué queremos limitar los posibles tamaños de los bloques? pues para que cuando se busquemos un bloque de un tamaño X (y X siempre será un valor dentro de los 128 prefijados), tendremos más posiblidades de encontrar un bloque contiguo de nuestro tamaño sin tener que dividir otro bloque en dos (como hacíamos con los montones normales).
La mala noticia es que, el mayor de estos 128 tamaños prefijados, es de 16.384 bytes, de decir: 16 KB. Así que, con los montones LFH, el bloque más grande que podemos reservar es de 16 KB. Si pedimos un valor mayor, se utilizarás la técnica que ya conocemos: la búsqueda en la cadena de libres y la división del bloque encontrado en dos trozos.
Puede ser interesante el uso de montones LFH, aunque, por ahora, estamos limitados a Windows XP y Windows .NET Server. Para versiones anteriores, podemos simular esta técnica utilizando las dos reglas que ya hemos visto:
utilizar bloques de un tamaño múltiplo de 2 y
reasignar bloques siempre al doble de su tamaño original.
Por cierto, para el que quiera utilizar los LFH, llamar a la nueva función HeapSetInformation, del siguiente modo:
{
ULONG lfh = 2;
hacer que el montón por defecto sea un LFH
SetHeapInformation( GetProcessHeap(), montón a cambiar
HeapCompatibilityInformation,
valor enumerado definido en "winnt.h"
&lfh,
sizeof(lfh) );
}
Ventajas del uso de montones
Utilizar los montones frente a la memoria virtual directamente, nos proporciona muchas ventajas:
- Se abstrae al programador de tareas de bajo nivel, como la manipulación de páginas y compromiso físico.
- A la hora de gestionar muchos objetos de distintos tamaños, se aprovecha el espacio mucho más eficientemente con montones que con memoria virtual, ya que ésta última está orientada a grandes bloques de memoria. Recordemos que la cantidad mínima que se podía direccionar con la función VirtualAlloc era de una página, 4 KB. Si no contásemos con la gestión de montones, cada variable (independientemente de su tipo y tamaño) ocuparía una página en memoria.
- Gestionar múltiples bloques de memoria con montones es más rápido que hacerlo directamente con las funciones de memoria virtual, ya que el gestor de montones intenta mantener el mayor número de páginas en RAM, ahorrándose así el tiempo de movimiento de páginas entre la memoria física y el archivo de intercambio en el disco duro.
- Con montones es posible situar ciertas variables dentro de un pequeño rango de direcciones virtuales, aumentando así el rendimiento y disminuyendo la fragmentación de la memoria.