📖 Origem: Real-Time Design Patterns: Robust Scalable Architecture for Real-Time Systems – Bruce Powel Douglass
Resumo (Abstract)
O Garbage Compactor Pattern é um padrão de gerenciamento de memória que reorganiza dinamicamente a alocação de memória para reduzir a fragmentação e garantir blocos contíguos disponíveis para uso. Em sistemas embarcados, onde a memória é um recurso extremamente limitado, esse padrão se torna essencial para maximizar o aproveitamento da RAM e evitar falhas inesperadas devido à fragmentação excessiva.
Problema a ser resolvido
Sistemas que fazem uso de alocação dinâmica de memória frequentemente sofrem com a fragmentação, onde blocos livres são dispersos na RAM, impossibilitando a alocação de grandes blocos de memória mesmo quando há espaço suficiente disponível. Esse problema é particularmente crítico em sistemas embarcados, onde a memória é limitada e cada falha na alocação pode levar a travamentos, reinicializações ou falhas de desempenho.
Além disso, a fragmentação pode tornar os tempos de resposta imprevisíveis, pois a busca por blocos de memória disponíveis se torna mais complexa. Sistemas de tempo real precisam de garantias de tempo de resposta determinístico, algo que pode ser comprometido caso a fragmentação cause variações no tempo de alocação. O Garbage Compactor Pattern resolve esse problema ao reorganizar periodicamente a memória, consolidando blocos livres em regiões contínuas e garantindo que grandes alocações possam ser feitas sem falhas.
Estrutura do Padrão
A estrutura desse padrão envolve um gerenciador de heap, que monitora a fragmentação da memória e decide quando realizar a compactação. A lista de blocos ocupados rastreia quais segmentos da RAM estão em uso, enquanto a lista de blocos livres gerencia os espaços disponíveis. Um mecanismo de realocação move os dados dos blocos alocados para consolidar as áreas livres, garantindo que a memória seja reorganizada sem impactar o funcionamento do sistema.
A compactação pode ser realizada em momentos estratégicos, como durante períodos de baixa atividade do sistema ou quando a fragmentação atinge um limite crítico. Esse processo pode ser implementado de forma assíncrona para minimizar impactos no desempenho.
Papéis de Colaboração (Collaborations Roles)
- Gerenciador de Heap: Supervisiona a alocação, desalocação e reorganização dos blocos de memória.
- Lista de Blocos Ocupados: Mantém um mapeamento dos blocos de memória em uso.
- Lista de Blocos Livres: Armazena informações sobre os segmentos disponíveis para alocação.
- Mecanismo de Realocação: Move os dados entre os blocos de memória para consolidar áreas livres.
- Cliente: Solicita alocações e liberações de memória conforme necessário.
- Segmento de Memória: Contém a memória real onde os blocos são armazenados e reorganizados.
Consequências
A aplicação do Garbage Compactor Pattern traz benefícios significativos para sistemas embarcados. Ele melhora a eficiência do uso da memória, garantindo que grandes blocos possam ser alocados sem falhas. Além disso, reduz os tempos de busca por espaço livre, melhorando a previsibilidade dos tempos de resposta, algo essencial em sistemas de tempo real.
Por outro lado, o processo de compactação pode ser custoso em termos de processamento, pois requer movimentação de dados na RAM. Se não for gerenciado corretamente, pode causar pausas no sistema que impactam a responsividade da aplicação. Outra consideração importante é que a movimentação dos blocos pode exigir a atualização de ponteiros e referências, o que pode ser complexo em algumas arquiteturas.
Estratégias de Implementação
A implementação do Garbage Compactor Pattern pode seguir diferentes abordagens, dependendo dos requisitos do sistema. Uma estratégia comum é a compactação periódica, onde a reorganização da memória ocorre em intervalos regulares ou quando a fragmentação atinge um limite predefinido. Esse método é útil em sistemas onde há momentos de baixa carga que podem ser aproveitados para a reorganização da memória.
Outra abordagem é a compactação sob demanda, onde a reorganização ocorre apenas quando uma tentativa de alocação falha devido à fragmentação. Essa estratégia minimiza o impacto no desempenho, pois a compactação é realizada apenas quando necessário. No entanto, pode causar atrasos inesperados no tempo de resposta quando a compactação precisa ser feita em um momento crítico.
Para garantir a integridade dos dados durante a realocação, é essencial utilizar mecanismos de proteção de ponteiros, garantindo que referências a blocos de memória sejam atualizadas corretamente. Outra técnica útil é a utilização de buffers temporários, permitindo que dados sejam copiados para uma área intermediária antes de serem reposicionados.
Padrões Relacionados
O Garbage Compactor Pattern está diretamente relacionado a outros padrões de gerenciamento de memória:
- Pool Allocation Pattern: Ambos lidam com otimização do uso de memória, mas o Pool Allocation evita a fragmentação desde o início, enquanto o Garbage Compactor atua quando a fragmentação já ocorreu.
- Fixed-Sized Buffer Pattern: Pode ser combinado com a compactação de memória para garantir um uso mais eficiente da RAM.
- Priority Ceiling Pattern: Pode ser usado para garantir que o processo de compactação ocorra sem interrupções indesejadas em sistemas críticos.
- Critical Section Pattern: Pode ser necessário para evitar acessos concorrentes durante a compactação.
Modelo de Amostragem (Exemplo de Código)

Segue um exemplo de implementação simplificada do Garbage Compactor Pattern em C:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define HEAP_SIZE 1024 // Tamanho total do heap
#define BLOCK_SIZE 32 // Tamanho de cada bloco de memória
#define NUM_BLOCKS (HEAP_SIZE / BLOCK_SIZE)
typedef struct MemoryBlock {
bool in_use;
void *ptr;
size_t size;
} MemoryBlock;
typedef struct Segment {
MemoryBlock blocks[NUM_BLOCKS];
uint8_t memory[HEAP_SIZE];
struct Segment *next;
} Segment;
typedef struct BufferedPtr {
void **ptr;
} BufferedPtr;
typedef struct GarbageCompactor {
Segment *activeSegment;
Segment *inactiveSegment;
} GarbageCompactor;
Segment segment1, segment2;
GarbageCompactor gc;
void init_segments() {
memset(&segment1, 0, sizeof(Segment));
memset(&segment2, 0, sizeof(Segment));
gc.activeSegment = &segment1;
gc.inactiveSegment = &segment2;
}
void *allocate_memory(size_t size) {
Segment *seg = gc.activeSegment;
for (int i = 0; i < NUM_BLOCKS; i++) {
if (!seg->blocks[i].in