Heap vs. Pilha para desenvolvedores Delphi

Chame a função "DoStackOverflow" uma vez de seu código e você receberá o EStackOverflow erro gerado pelo Delphi com a mensagem "estouro de pilha".


função DoStackOverflow: inteiro;

início

 resultado: = 1 + DoStackOverflow;

fim;

O que é essa "pilha" e por que há um estouro usando o código acima?

Portanto, a função DoStackOverflow se chama recursivamente - sem uma "estratégia de saída" - continua girando e nunca sai.

Uma solução rápida, você faria, é limpar o bug óbvio que você possui e garantir que a função exista em algum momento (para que seu código possa continuar sendo executado a partir de onde você chamou a função).

Você segue em frente e nunca olha para trás, sem se importar com o erro / exceção, como agora está resolvido.

No entanto, a questão permanece: o que é essa pilha e por que existe um estouro?

Memória nas suas aplicações Delphi

Quando você inicia a programação no Delphi, pode experimentar um bug como o descrito acima, resolvê-lo e seguir em frente. Este está relacionado à alocação de memória. Na maioria das vezes, você não se importaria com a alocação de memória, desde que

instagram viewer
liberte o que você cria.

À medida que você ganha mais experiência no Delphi, você começa a criar suas próprias classes, instancia-las, se preocupando com o gerenciamento de memória e similares.

Você chegará ao ponto em que irá ler, na Ajuda, algo como "Variáveis ​​locais (declaradas dentro de procedimentos e funções) residem no aplicativo pilha." e também As classes são tipos de referência, portanto, não são copiadas na atribuição, são passadas por referência e são alocadas no amontoar.

Então, o que é "pilha" e o que é "pilha"?

Stack vs. Montão

Executando seu aplicativo no Windows, há três áreas na memória em que seu aplicativo armazena dados: memória global, heap e pilha.

Variáveis ​​globais (seus valores / dados) são armazenadas na memória global. A memória para variáveis ​​globais é reservada pelo seu aplicativo quando o programa inicia e permanece alocada até o encerramento do programa. A memória para variáveis ​​globais é chamada "segmento de dados".

Como a memória global é alocada e liberada apenas uma vez no encerramento do programa, não nos importamos com isso neste artigo.

Pilha e pilha são onde a alocação dinâmica de memória ocorre: quando você cria uma variável para uma função, quando você cria uma instância de uma classe quando envia parâmetros para uma função e usa / passa seu resultado valor.

O que é a pilha?

Quando você declara uma variável dentro de uma função, a memória necessária para reter a variável é alocada na pilha. Você simplesmente escreve "var x: número inteiro", usa "x" em sua função e, quando a função é encerrada, você não se importa com alocação de memória nem liberação. Quando a variável sai do escopo (o código sai da função), a memória que foi tirada na pilha é liberada.

A memória da pilha é alocada dinamicamente usando a abordagem LIFO ("last in first out").

Dentro Programas Delphi, a pilha de memória é usada por

  • Variáveis ​​de rotina local (método, procedimento, função).
  • Parâmetros de rotina e tipos de retorno.
  • Função da API do Windows chamadas.
  • Registros (é por isso que você não precisa criar explicitamente uma instância de um tipo de registro).

Você não precisa liberar explicitamente a memória na pilha, pois a memória é alocada automaticamente para você quando, por exemplo, declara uma variável local para uma função. Quando a função sair (às vezes antes mesmo devido à otimização do compilador Delphi), a memória da variável será automaticamente liberada magicamente.

Tamanho da memória da pilha é, por padrão, grande o suficiente para os seus programas (tão complexos quanto eles) Delphi. Os valores "Tamanho máximo da pilha" e "Tamanho mínimo da pilha" nas opções do Vinculador para o seu projeto especificam valores padrão - em 99,99%, você não precisaria alterar isso.

Pense em uma pilha como uma pilha de blocos de memória. Quando você declara / usa uma variável local, o gerenciador de memória Delphi seleciona o bloco de cima para cima, usa-o e, quando não for mais necessário, ele retornará à pilha.

Tendo a memória variável local usada na pilha, as variáveis ​​locais não são inicializadas quando declaradas. Declare uma variável "var x: integer" em alguma função e tente ler o valor ao inserir a função - x terá um valor diferente de zero "estranho". Portanto, sempre inicialize (ou defina o valor) para suas variáveis ​​locais antes de ler o valor delas.

Devido ao LIFO, as operações de pilha (alocação de memória) são rápidas, pois são necessárias apenas algumas operações (push, pop) para gerenciar uma pilha.

O que é Heap?

Um heap é uma região da memória na qual a memória alocada dinamicamente é armazenada. Quando você cria uma instância de uma classe, a memória é alocada do heap.

Nos programas Delphi, a memória heap é usada por / quando

  • Criando uma instância de uma classe.
  • Criando e redimensionando matrizes dinâmicas.
  • Alocar explicitamente memória usando GetMem, FreeMem, New e Dispose ().
  • Usando seqüências de caracteres ANSI / wide / Unicode, variantes, interfaces (gerenciadas automaticamente pelo Delphi).

A memória heap não possui um layout agradável, onde haveria alguma ordem na alocação de blocos de memória. Heap parece uma lata de bolas de gude. A alocação de memória do heap é aleatória, um bloco daqui do que um bloco dali. Portanto, as operações de heap são um pouco mais lentas que as da pilha.

Quando você pede um novo bloco de memória (ou seja, cria uma instância de uma classe), o gerenciador de memória Delphi cuida disso: você receberá um novo bloco de memória ou um usado e descartado.

O heap consiste em toda a memória virtual (RAM e espaço em disco).

Alocação manual de memória

Agora que a memória está limpa, você pode (com segurança) (na maioria dos casos) ignorar o que foi dito acima e simplesmente continuar escrevendo os programas Delphi como fez ontem.

Obviamente, você deve estar ciente de quando e como alocar / liberar memória manualmente.

O "EStackOverflow" (desde o início do artigo) foi gerado porque a cada chamada para DoStackOverflow, um novo segmento de memória foi usado na pilha e a pilha tem limitações. Tão simples como isso.