Alocação de memória baseada em pilha - Stack-based memory allocation
Pilhas em arquiteturas de computação são regiões da memória onde os dados são adicionados ou removidos de uma maneira LIFO (last-in-first-out) .
Na maioria dos sistemas de computador modernos, cada thread tem uma região reservada de memória chamada de pilha. Quando uma função é executada, ela pode adicionar alguns de seus dados de estado local ao topo da pilha; quando a função é encerrada, ela é responsável por remover esses dados da pilha. No mínimo, a pilha de um thread é usada para armazenar a localização de um endereço de retorno fornecido pelo chamador para permitir que as instruções de retorno retornem ao local correto. A pilha é freqüentemente usada para armazenar variáveis de comprimento fixo local para as funções atualmente ativas. Os programadores podem ainda escolher explicitamente usar a pilha para armazenar dados locais de comprimento variável. Se uma região da memória estiver na pilha do encadeamento, diz-se que essa memória foi alocada na pilha, ou seja, alocação de memória baseada na pilha .
Vantagens e desvantagens
Como os dados são adicionados e removidos da maneira last-in-first-out, a alocação de memória baseada em pilha é muito simples e normalmente muito mais rápida do que a alocação de memória baseada em heap (também conhecida como alocação de memória dinâmica ) normalmente alocada via malloc. Outro recurso é que a memória na pilha é automaticamente, e de forma muito eficiente, recuperada quando a função é encerrada, o que pode ser conveniente para o programador se os dados não forem mais necessários. (O mesmo se aplica a longjmp se ele se moveu para um ponto antes da chamada alloca
acontecer.) Se, entretanto, os dados precisam ser mantidos de alguma forma, então eles devem ser copiados da pilha para o heap antes da saída da função. Portanto, a alocação baseada em pilha é adequada para dados temporários ou dados que não são mais necessários após a saída da função atual.
O tamanho de pilha atribuído a um thread pode ser tão pequeno quanto apenas alguns bytes em algumas CPUs pequenas. Alocar mais memória na pilha do que o disponível pode resultar em um travamento devido ao estouro da pilha . É também por isso que as funções que usam alloca
geralmente são impedidas de serem sequenciadas: se tal função for sequenciada em um loop, o chamador sofrerá de um aumento imprevisto no uso da pilha, tornando um estouro muito mais provável.
A alocação baseada em pilha também pode causar pequenos problemas de desempenho: ela leva a frames de pilha de tamanho variável, de modo que ponteiros de pilha e frame precisam ser gerenciados (com frames de pilha de tamanho fixo, um deles é redundante). Isso geralmente é muito mais barato do que ligar malloc
e de free
qualquer maneira. Em particular, se a função atual contém chamadas para alloca e blocos contendo dados locais de comprimento variável, então ocorre um conflito entre as tentativas de alloca de aumentar o quadro de pilha atual até que a função atual saia contra a necessidade do compilador de colocar variáveis locais de comprimento variável no mesmo local no frame da pilha. Este conflito é normalmente resolvido criando uma cadeia separada de armazenamento de heap para cada chamada para alloca (consulte: https://code.woboq.org/gcc/libiberty/alloca.c.html ). A cadeia registra a profundidade da pilha em que cada alocação ocorre, chamadas subsequentes para alloca em qualquer função cortam essa cadeia até a profundidade da pilha atual para, eventualmente (mas não imediatamente), liberar qualquer armazenamento nesta cadeia. Uma chamada para alloca com um argumento de zero também pode ser usada para acionar a liberação de memória sem alocar mais memória. Como consequência desse conflito entre alloca e o armazenamento de variável local, usar alloca pode não ser mais eficiente do que usar malloc.
Interface do sistema
Muitos sistemas semelhantes ao Unix, bem como o Microsoft Windows, implementam uma função chamada alloca
para alocar dinamicamente a memória da pilha de uma forma semelhante à baseada em heap malloc
. Um compilador normalmente o traduz em instruções embutidas que manipulam o ponteiro da pilha, semelhante a como as matrizes de comprimento variável são tratadas. Embora não haja necessidade de liberar explicitamente a memória, existe o risco de comportamento indefinido devido ao estouro da pilha. A função estava presente em sistemas Unix já em 32 / V (1978), mas não faz parte do Padrão C ou de qualquer padrão POSIX .
Uma versão mais segura do alloca
chamado _malloca
, que relata erros, existe no Microsoft Windows. Requer o uso de _freea
. O gnulib fornece uma interface equivalente, embora em vez de lançar uma exceção SEH no overflow, ele delega malloc
quando um tamanho overlarge é detectado. Um recurso semelhante pode ser emulado usando contabilidade manual e verificação de tamanho, como nos usos de alloca_account
in glibc.
Algumas famílias de processadores, como o x86 , têm instruções especiais para manipular a pilha do thread em execução no momento. Outras famílias de processadores, incluindo PowerPC e MIPS , não têm suporte explícito de pilha, mas, em vez disso, contam com a convenção e delegam o gerenciamento de pilha à interface binária de aplicativo (ABI) do sistema operacional .
Veja também
- Variável automática
- Variável estática
- Pilha de chamadas
- Alocação de memória dinâmica
- Estouro do buffer de pilha
- Máquina de empilhar
- Estouro de pilha