Os PCs de hoje têm uma grande quantidade de RAM física, mas ainda assim, o tamanho da pilha do C # é de apenas 1 MB para processos de 32 bits e 4 MB para processos de 64 bits ( capacidade de pilha em C # ).
Por que o tamanho da pilha no CLR ainda é tão limitado?
E por que é exatamente 1 MB (4 MB) (e não 2 MB ou 512 KB)? Por que foi decidido usar esses valores?
Estou interessado em considerações e razões por trás dessa decisão .
c#
stack
clr
stack-size
Nikolay Kostov
fonte
fonte
Thread
construtor. MAS, isso levanta a questão, por que você precisa de uma pilha maior?1572864
tamanho de pilha padrão de bytes (recuperado usando GetCurrentThreadStackLimits Win32 API). Sou capaz destackalloc
aproximadamente1500000
bytes sem StackOverflowException.Respostas:
Você está olhando para o cara que fez essa escolha. David Cutler e sua equipe selecionaram um megabyte como o tamanho de pilha padrão. Nada a ver com .NET ou C #, isso foi resolvido quando eles criaram o Windows NT. Um megabyte é o que ele escolhe quando o cabeçalho EXE de um programa ou a chamada do winapi CreateThread () não especifica o tamanho da pilha explicitamente. Que é a forma normal, quase qualquer programador deixa que o sistema operacional escolha o tamanho.
Essa escolha provavelmente é anterior ao design do Windows NT, a história é muito obscura sobre isso. Seria bom se Cutler escrevesse um livro sobre isso, mas ele nunca foi escritor. Ele tem sido extraordinariamente influente na maneira como os computadores funcionam. Seu primeiro design de sistema operacional foi RSX-11M, um sistema operacional de 16 bits para computadores DEC (Digital Equipment Corporation). Ele influenciou fortemente o CP / M de Gary Kildall, o primeiro sistema operacional decente para microprocessadores de 8 bits. O que influenciou fortemente o MS-DOS.
Seu próximo projeto foi o VMS, um sistema operacional para processadores de 32 bits com suporte para memória virtual. Muito bem sucedido. Seu próximo foi cancelado pela DEC na época em que a empresa começou a se desintegrar, não sendo capaz de competir com hardware de PC barato. Cue Microsoft, eles lhe fizeram uma oferta que ele não podia recusar. Muitos de seus colegas de trabalho também aderiram. Eles trabalharam no VMS v2, mais conhecido como Windows NT. DEC ficou chateado com isso, o dinheiro mudou de mãos para resolver o problema. Se o VMS já escolheu um megabyte é algo que não sei, só conheço o RSX-11 bem o suficiente. Não é improvável.
Chega de história. Um megabyte é muito , um thread real raramente consome mais do que alguns poucos kilobytes. Portanto, um megabyte é realmente um desperdício. No entanto, é o tipo de desperdício que você pode pagar em um sistema operacional de memória virtual paginada por demanda, aquele megabyte é apenas memória virtual . Apenas números para o processador, um para cada 4096 bytes. Na verdade, você nunca usa a memória física, a RAM da máquina, até que a endereça de fato.
É extremamente excessivo em um programa .NET porque o tamanho de um megabyte foi originalmente escolhido para acomodar programas nativos. Que tendem a criar grandes quadros de pilha, armazenando strings e buffers (arrays) na pilha também. Infame por ser um vetor de ataque de malware, um estouro de buffer pode manipular o programa com dados. Não é a forma como os programas .NET funcionam, strings e arrays são alocados no heap do GC e a indexação é verificada. A única maneira de alocar espaço na pilha com C # é com a palavra-chave stackalloc não segura .
O único uso não trivial da pilha no .NET é por jitter. Ele usa a pilha de seu thread para compilar o MSIL para o código de máquina just-in-time. Eu nunca vi ou verifiquei quanto espaço ele requer, depende da natureza do código e se o otimizador está habilitado ou não, mas algumas dezenas de kilobytes é um palpite aproximado. Caso contrário, este site recebeu o nome, um estouro de pilha em um programa .NET é bastante fatal. Não há espaço suficiente (menos de 3 kilobytes) para executar o JIT confiável de qualquer código que tente capturar a exceção. Kaboom para desktop é a única opção.
Por último, mas não menos importante, um programa .NET faz algo bastante improdutivo com a pilha. O CLR irá comprometer a pilha de um thread. Essa é uma palavra cara que significa que ela não apenas reserva o tamanho da pilha, mas também garante que o espaço seja reservado no arquivo de paginação do sistema operacional para que a pilha possa sempre ser trocada quando necessário. Deixar de confirmar é um erro fatal e encerra um programa incondicionalmente. Isso só acontece em máquinas com pouquíssima RAM que executa muitos processos, tal máquina terá se transformado em melaço antes que os programas comecem a morrer. Um possível problema há mais de 15 anos, não hoje. Os programadores que ajustam seu programa para agir como um carro de corrida de F1 usam o
<disableCommitThreadStack>
elemento em seu arquivo .config.Fwiw, Cutler não parou de projetar sistemas operacionais. Essa foto foi feita enquanto ele trabalhava no Azure.
Update, notei que o .NET não confirma mais a pilha. Não tenho certeza de quando ou por que isso aconteceu, já faz muito tempo desde que verifiquei. Acho que essa mudança de design aconteceu em algum lugar perto do .NET 4.5. Mudança bastante sensata.
fonte
The only way to allocate space on the stack with C# is with the unsafe stackalloc keyword.
- As variáveis locais, por exemplo, umaint
declarada dentro de um método, não são armazenadas na pilha? Eu acho que eles são.maxStackSize
de um segmento? Não consegui encontrar no [MSDN] ( msdn.microsoft.com/en-us/library/5cykbwz4(v=vs.110).aspx ). Com base em seus comentários, parece que o uso da pilha é o mínimo absoluto e posso usar o menor valor para acomodar o máximo possível de threads. Obrigado.O tamanho da pilha reservada padrão é especificado pelo vinculador e pode ser substituído pelos desenvolvedores por meio da alteração do valor PE no momento do link ou para um segmento individual especificando o
dwStackSize
parâmetro para aCreateThread
função WinAPI.Se você criar um thread com o tamanho da pilha inicial maior ou igual ao tamanho da pilha padrão, ele será arredondado para o múltiplo mais próximo de 1 MB.
Por que o valor é igual a 1 MB para processos de 32 bits e 4 MB para 64 bits? Acho que você deve perguntar aos desenvolvedores que projetaram o Windows ou esperar até que alguém responda à sua pergunta.
Provavelmente Mark Russinovich sabe disso e você pode contatá- lo. Talvez você possa encontrar essas informações em seus livros do Windows Internals anteriores à sexta edição, que descreve menos informações sobre pilhas em vez de seu artigo . Ou talvez Raymond Chen conheça os motivos, já que escreve coisas interessantes sobre os componentes internos do Windows e sua história. Ele também pode responder à sua pergunta, mas você deve postar uma sugestão na Caixa de sugestões .
Mas, neste momento, tentarei explicar algumas razões prováveis pelas quais a Microsoft escolheu esses valores usando os blogs do MSDN, Mark e Raymond.
Os padrões têm esses valores provavelmente porque nos primeiros tempos os PCs eram lentos e a alocação de memória na pilha era muito mais rápida do que a alocação de memória no heap. E como as alocações de pilha eram muito mais baratas, elas eram usadas, mas exigia um tamanho de pilha maior.
Portanto, o valor era o tamanho de pilha reservado ideal para a maioria dos aplicativos. É ideal porque permite fazer muitas chamadas aninhadas e alocar memória na pilha para passar estruturas para funções de chamada. Ao mesmo tempo, permite criar muitos tópicos.
Hoje em dia, esses valores são usados principalmente para compatibilidade com versões anteriores, porque as estruturas que são passadas como parâmetros para funções WinAPI ainda são alocadas na pilha. Mas se você não estiver usando alocações de pilha, o uso de pilha de uma thread será significativamente menor do que o padrão de 1 MB e é um desperdício, como Hans Passant mencionou. E para evitar isso, o SO compromete apenas a primeira página da pilha (4 KB), se outra não for especificada no cabeçalho PE da aplicação. Outras páginas são alocadas sob demanda.
Alguns aplicativos substituem o espaço de endereço reservado e inicialmente comprometidos com a otimização do uso da memória. Como exemplo, o tamanho máximo da pilha de um thread de processo nativo do IIS é 256 KB ( KB932909 ). E esta diminuição dos valores padrão é recomendada pela Microsoft:
Fontes:
fonte