Alocação de memória baseada em pilha - Stack-based memory allocation

Uma pilha típica, armazenando dados locais e informações de chamada para chamadas de procedimento aninhado (não necessariamente procedimentos aninhados ). Esta pilha cresce para baixo a partir de sua origem. O ponteiro da pilha aponta para o datum mais alto atual da pilha. Uma operação push diminui o ponteiro e copia os dados para a pilha; uma operação pop copia os dados da pilha e, a seguir, incrementa o ponteiro. Cada procedimento chamado no programa armazena informações de retorno de procedimento (em amarelo) e dados locais (em outras cores), empurrando-os para a pilha.

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 allocaacontecer.) 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 allocageralmente 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 malloce de freequalquer 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 allocapara 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 allocachamado _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 mallocquando um tamanho overlarge é detectado. Um recurso semelhante pode ser emulado usando contabilidade manual e verificação de tamanho, como nos usos de alloca_accountin 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

Referências