Em resumo
Uma pilha é usada para alocação de memória estática e uma pilha para alocação de memória dinâmica, ambas armazenadas na RAM do computador.
Em detalhe
A pilha
A pilha é uma estrutura de dados "LIFO" (última entrada, primeira saída), que é gerenciada e otimizada pela CPU de perto. Toda vez que uma função declara uma nova variável, ela é "empurrada" para a pilha. Toda vez que uma função sai, todas as variáveis colocadas na pilha por essa função são liberadas (ou seja, são excluídas). Depois que uma variável de pilha é liberada, essa região de memória fica disponível para outras variáveis de pilha.
A vantagem de usar a pilha para armazenar variáveis é que a memória é gerenciada por você. Você não precisa alocar a memória manualmente ou liberá-la quando não precisar mais dela. Além disso, como a CPU organiza a memória da pilha com tanta eficiência, a leitura e a gravação das variáveis da pilha são muito rápidas.
Mais pode ser encontrado aqui .
The Heap
O heap é uma região da memória do computador que não é gerenciada automaticamente para você e não é gerenciada com tanta rigidez pela CPU. É uma região de memória mais flutuante (e é maior). Para alocar memória no heap, você deve usar malloc () ou calloc (), que são funções C internas. Depois de alocar memória no heap, você é responsável por usar free () para desalocar a memória quando não precisar mais dela.
Se você não conseguir fazer isso, seu programa terá o que é conhecido como vazamento de memória. Ou seja, a memória no heap ainda será reservada (e não estará disponível para outros processos). Como veremos na seção de depuração, existe uma ferramenta chamada Valgrind que pode ajudá-lo a detectar vazamentos de memória.
Diferentemente da pilha, o heap não possui restrições de tamanho em tamanho variável (além das óbvias limitações físicas do seu computador). A memória da pilha é um pouco mais lenta para ser lida e gravada, porque é necessário usar ponteiros para acessar a memória na pilha. Falaremos sobre indicadores em breve.
Diferentemente da pilha, as variáveis criadas no heap são acessíveis por qualquer função, em qualquer lugar do seu programa. Variáveis de heap são essencialmente globais em escopo.
Mais pode ser encontrado aqui .
As variáveis alocadas na pilha são armazenadas diretamente na memória e o acesso a essa memória é muito rápido, e sua alocação é tratada quando o programa é compilado. Quando uma função ou método chama outra função que, por sua vez, chama outra função, etc., a execução de todas essas funções permanece suspensa até a última função retornar seu valor. A pilha é sempre reservada em uma ordem LIFO, o bloco reservado mais recente é sempre o próximo bloco a ser liberado. Isso torna muito simples acompanhar a pilha, liberar um bloco da pilha nada mais é do que ajustar um ponteiro.
As variáveis alocadas no heap têm sua memória alocada em tempo de execução e o acesso a essa memória é um pouco mais lento, mas o tamanho do heap é limitado apenas pelo tamanho da memória virtual. Os elementos da pilha não têm dependências entre si e sempre podem ser acessados aleatoriamente a qualquer momento. Você pode alocar um bloco a qualquer momento e liberá-lo a qualquer momento. Isso torna muito mais complexo acompanhar quais partes do heap estão alocadas ou livres a qualquer momento.
Você pode usar a pilha se souber exatamente a quantidade de dados que precisa alocar antes do tempo de compilação e ela não for muito grande. Você pode usar o heap se não souber exatamente quantos dados precisará no tempo de execução ou se precisar alocar muitos dados.
Em uma situação multithread, cada thread terá sua própria pilha completamente independente, mas eles compartilharão o heap. A pilha é específica do segmento e a pilha é específica do aplicativo. É importante considerar a pilha no tratamento de exceções e nas execuções de encadeamento.
Cada encadeamento obtém uma pilha, enquanto normalmente há apenas um heap para o aplicativo (embora não seja incomum ter vários heap para diferentes tipos de alocação).
No tempo de execução, se o aplicativo precisar de mais heap, ele poderá alocar memória da memória livre e se a pilha precisar de memória, poderá alocar memória da memória alocada para o aplicativo.
Ainda, mais detalhes são dados aqui e aqui .
Agora chegue às respostas da sua pergunta .
Até que ponto eles são controlados pelo SO ou pelo tempo de execução do idioma?
O SO aloca a pilha para cada encadeamento no nível do sistema quando o encadeamento é criado. Normalmente, o sistema operacional é chamado pelo runtime do idioma para alocar o heap para o aplicativo.
Mais pode ser encontrado aqui .
Qual é o seu escopo?
Já entregue no topo.
"Você pode usar a pilha se souber exatamente quantos dados precisa alocar antes do tempo de compilação e eles não forem muito grandes. Você pode usar o heap se não souber exatamente quantos dados precisará no tempo de execução ou se você precisa alocar muitos dados ".
Mais pode ser encontrado aqui .
O que determina o tamanho de cada um deles?
O tamanho da pilha é definido pelo SO quando um encadeamento é criado. O tamanho do heap é definido na inicialização do aplicativo, mas pode aumentar conforme o espaço necessário (o alocador solicita mais memória do sistema operacional).
O que torna um mais rápido?
A alocação de pilha é muito mais rápida, pois tudo o que realmente faz é mover o ponteiro da pilha. Usando pools de memória, você pode obter desempenho comparável com a alocação de heap, mas isso vem com uma leve complexidade adicional e suas próprias dores de cabeça.
Além disso, pilha versus pilha não é apenas uma consideração de desempenho; também informa muito sobre a vida útil esperada dos objetos.
Detalhes podem ser encontrados aqui .