HotXLS almacena las celdas de las hojas en bloques compactos de 256 filas, resuelve el formato de filas, columnas y rectángulos mediante solapados perezosos por intervalos en lugar de crear objetos de celda, y vuelca cada fila directamente al flujo deflate del paquete al guardar. Juntos, esos tres cambios deciden el perfil de memoria de un libro grande: el pico de uso sigue la fila individual más grande en lugar del tamaño del XML completo de la hoja
El motivo por el que esto importa tiene una forma que todo desarrollador de hojas de cálculo acaba encontrando. Un usuario formatea una columna entera —un clic, un millón de celdas— y un modelo de objetos ingenuo responde asignando un millón de objetos de celda para guardar un único índice de formato numérico. El archivo en disco sigue siendo diminuto porque el formato XLSX lo expresa como una única entrada <col>. El proceso no sigue siendo diminuto en absoluto
¿Por qué formatear una columna cuesta más memoria que llenarla?
Porque el formato no tiene datos que justifiquen el objeto. Una celda con valor tiene que existir en algún sitio. Una celda vacía pero con estilo existe solo para portar un índice de estilo, y materializar millones de ellas es la forma clásica en que una aplicación Delphi de hoja de cálculo se queda sin espacio de direcciones sobre un archivo que Excel abre al instante
Los solapados de estilo por intervalos eliminan esa necesidad. Una instrucción de formato de fila, columna o rectángulo se almacena una sola vez como un rango más las partes de estilo que aporta, y se resuelve perezosamente cuando una celda de ese rango se accede de verdad. Los solapados sobreviven a las ediciones estructurales —insertar una fila dentro de un bloque formateado mueve el intervalo en lugar de reconstruirlo— y hacen el viaje de ida y vuelta como entradas compactas de columna, fila y celda de solo estilo, que es justamente como las escribe Excel
var
Sheet: TXLSXWorksheet;
State: TXLSXCellStyleState;
begin
Sheet := Workbook.Sheets[1];
// Style indexes come from the workbook style pools, e.g. from a cell
// you have already formatted the way you want the range to look
State := BuildStateFromTemplateCell(Sheet.Cells.Item[1, 1]);
// Format columns B..D without creating a single empty cell object
Sheet.StyleOverlays.Add(1, 2, MaxRowIndex, 4,
[xfpNumberFormat, xfpAlignment], State);
end;
TXLSXFormatParts es el conjunto que decide qué aporta un solapado: xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment y xfpProtection. Nombrar solo las partes que interesas es lo que permite que los solapados se capen de forma sensata —un solapado de columna que aporta un formato numérico no pleitea con un solapado de fila que aporta un relleno, porque ninguno reclama la parte del otro—
Qué aporta el bloque de 256 filas
Localidad. Las celdas se mantienen en bloques de 256 filas con handles públicos estables, serializadas en orden de filas, de modo que escribir una hoja recorre la memoria en el orden en que emitirá bytes en lugar de perseguir punteros por todo el montón. Los handles estables importan a la superficie de la API: un handle que sujeta un llamador sigue siendo válido a través de la reorganización interna que realiza la disposición de bloques, lo que convierte la representación compacta en un detalle de implementación en lugar de un cambio rompedor
La compactación del fondo de estilos corre en paralelo. Antes de cada guardado, se descartan las fuentes, los rellenos, los bordes, los formatos numéricos, las alineaciones y las protecciones que ninguna celda referencie. Los libros longevos acumulan registros de estilo sin referenciar igual que los documentos longevos acumulan estilos sin usar, y un libro que un usuario ha editado durante una hora puede portar cientos de ellos hacia un archivo del que nadie los leerá jamás
Guardado por flujo de filas, y cuándo no aplica
Con StreamingWrite activado —el valor predeterminado— cada fila de la hoja se escribe directamente en el flujo deflate del paquete. La alternativa, que es lo que el flag desactiva, construye primero el XML completo de la hoja y lo comprime después, de modo que el pico de memoria escala con la hoja entera. El streaming hace que escale con una fila
Las cadenas compartidas y las partes auxiliares siguen la misma disciplina a través de un serializador UTF-8 reutilizable que emite entradas de una en una, acotando el pico de memoria por la entrada individual más grande en lugar de por la parte entera. Eso cubre la tabla de cadenas compartidas y los registros de tabla dinámica, que en un libro analítico amplio suelen ser más grandes que cualquier hoja individual
var
Workbook: TXLSXWorkbook;
begin
Workbook := TXLSXWorkbook.Create(nil);
try
Workbook.Open('ledger-2026.xlsx');
// StreamingWrite defaults to True; turn it off only when a downstream
// step requires the whole worksheet XML to exist before compression
Workbook.StreamingWrite := True;
Workbook.SaveAs('ledger-2026-out.xlsx');
finally
Workbook.Free;
end;
end;
Dejalo activado salvo que tengas un motivo concreto para no hacerlo. El cauce no streaming existe para los casos en que otra cosa del flujo necesita el XML ensamblado, y pagarlo por defecto es pagar por un caso que la mayoría de aplicaciones nunca padece
Cómo saber si los solapados se están usando de verdad
Observa el recuento de celdas, no la gráfica de memoria. Si una hoja informa de un número plausible de celdas físicas después de aplicar formato extenso, los solapados están haciendo su trabajo. Si el recuento salta al tamaño del rango formateado, algo del cauce de código ha materializado las celdas —normalmente un bucle que lee cada celda del rango para comprobar su estilo, lo que fuerza la resolución celda a celda y derrota todo el arreglo—
Resuelve un estilo cuando necesites el formato efectivo de una celda. No resuelvas el estilo de un millón de celdas para descubrir que la columna tiene un formato numérico; pregúntale al solapado. La misma regla aplica a la escritura: asigna valores a las celdas que tienen valor, y deja que el formato siga siendo un intervalo
Dónde se va el resto de la memoria
Una vez que celdas y estilos son compactos, los siguientes mayores consumidores en un libro grande son la tabla de cadenas compartidas y las partes satélite que porte el archivo —cachés de tabla dinámica, dibujos, XML conservado de partes que el modelo de objetos no modela—. Esas tienen sus propias estrategias, y la respuesta honesta es que ningún ajuste único las resuelve todas a la vez
Si tu cuello de botella es abrir en lugar de guardar, la carga selectiva es la palanca: el recorrido por la carga de hojas solo metadatos y selectiva cubre cómo leer un libro sin pagar las hojas que no vas a tocar. Para el rendimiento de la ruta de lectura en archivos muy grandes, consulta las notas sobre el análisis XLSX en paralelo y el asignador de memoria, y para cargas de solo salida que jamás necesitan un modelo de objetos, las escrituras por streaming para trabajos por lotes del servidor suelen encajar mejor que cualquier cantidad de afinamiento aquí
HotXLS lee y escribe XLS y XLSX desde código nativo de Delphi y C++Builder sin Excel instalado ni automatización OLE, que es justamente lo que hace estas características de memoria observables y controlables en primer lugar; la página del componente de hoja de cálculo HotXLS lista los formatos admitidos y las versiones de RAD Studio