Esta pergunta é motivada pelos seguintes fenômenos observados regularmente para os quais gostaria de encontrar uma explicação:
- O commit atual é regularmente maior que o uso físico + tamanho do arquivo de paginação. O que há com isso? Isso não deveria ser impossível? [Parece que isso pode ser por causa da compressão. O que transforma a questão em: Por que o limite de commit não aumenta ou algo assim? Ou seja, qual é o ponto de compactação se isso não ajuda no uso de memória?]
- Às vezes, isso atinge níveis extremos onde o commit atual é mais que o dobro do uso da memória física!
- Quando a carga de commit é preenchida e o Windows começa a me pedir para fechar coisas, na maioria das vezes a memória física fica em torno de 60%. Isso parece terrivelmente ineficiente.
Isso ocorre no Windows 10, conforme relatado pelo Process Explorer.
A pergunta final que gostaria de responder é: Posso deixar de inflar artificialmente meu arquivo de paginação para níveis que meu SSD carente de espaço está mal equipado para lidar, apenas para que eu possa realmente utilizar minha memória física com eficácia? (Ou mesmo que não estivesse tão cheio. Ou seja, gostaria de evitar sugestões como "Faça X/Y/Z no seu arquivo de paginação".)
Responder1
Na verdade, isso é bastante simples, uma vez que você entende que a cobrança de commit representa apenaspotencial- ainda "disponível garantida se você quiser" - uso de memória virtual, enquanto o "conjunto de trabalho privado" - que é essencialmente a RAM usada pela memória "comprometida" - érealusar, assim como o espaço do arquivo de paginação. (Mas isso não é tudo sobre o uso de RAM, porque há outras coisas que usam RAM).
Vamos supor que estamos falando de sistemas de 32 bits, então o espaço máximo de endereço virtual disponível para cada processo é normalmente de 2 GiB. (Não há diferença substancial emqualquerdos seguintes para sistemas de 64 bits, exceto que os endereços e tamanhos podem ser maiores - muito maiores.)
Agora suponha que um programa em execução em um processo use VirtualAlloc (uma API Win32) para "confirmar" 2 MiB de memória virtual. Como seria de esperar, isso aparecerá como 2 MiB adicionais de cobrança de confirmação e há 2 MiB a menos de espaço de endereço virtual disponível no processo para alocações futuras.
Mas ainda não usará nenhuma memória física (RAM)!
A chamada do VirtualAlloc retornará ao chamador o endereço inicial da região alocada; a região estará em algum lugar no intervalo de 0x10000 a 0x7FFEFFFF, ou seja, cerca de 2 GiB. (O primeiro e o último 64KiB, ou 0x10000 em hexadecimal, de vas em cada processo nunca são atribuídos.)
Mas, novamente - não há uso físico real de 2 MiB dearmazenarainda! Nem na RAM, nem mesmo no arquivo de paginação. (Existe uma pequena estrutura chamada "Descritor de endereço virtual" que descreve o início va e o comprimento da região privada comprometida.)
Então aí está! A cobrança de confirmação aumentou, mas o uso da memória física não.
Isso é fácil de demonstrar com a ferramenta sysinternals testlimit
.
Algum tempo depois, digamos que o programa armazene algo (ou seja, uma operação de gravação na memória) naquela região (não importa onde). Ainda não existe memória física em nenhuma região, portanto tal acesso incorrerá em umfalha de página. Em resposta a isso, o gerenciador de memória do sistema operacional, especificamente a rotina do manipulador de falhas de página (o "pager" para abreviar... é chamado MmAccessFault), irá:
- alocar uma página física previamente "disponível"
- configure a entrada da tabela de páginas para a página virtual que foi acessada para associar o número da página virtual ao número da página física recém-atribuído
- adicione a página física ao processo privadoConjunto de trabalho
- e descartar a falha de página, fazendo com que a instrução que gerou a falha seja tentada novamente.
Você agora "culpou" uma página (4 KiB) no processo. E o uso da memória física aumentará de acordo e a RAM "disponível" diminuirá. A cobrança de confirmação não muda.
Algum tempo depois, se essa página não for referenciada há algum tempo e a demanda por RAM for alta, isso poderá acontecer:
- o sistema operacional remove a página do conjunto de trabalho do processo.
- porque foi gravado desde que foi trazido para o conjunto de trabalho, ele é colocado na lista de páginas modificada (caso contrário, iria para a lista de páginas de espera). A entrada da tabela de páginas ainda reflete o número físico da página da RAM, mas agora tem seu bit "válido" limpo, portanto, na próxima vez que for referenciada, ocorrerá uma falha de página
- quando a lista de páginas modificada atinge um pequeno limite, umescritor de página modificadothread no processo "Sistema" acorda e salva o conteúdo das páginas modificadas no arquivo de paginação (supondo que você tenha um) e ...
- retira essas páginas da lista modificada e as coloca na lista de espera. Eles agora são considerados parte da RAM “disponível”; mas por enquanto eles ainda possuem seus conteúdos originais de quando estavam em seus respectivos processos. Novamente, a taxa de confirmação não muda, mas o uso de RAM e o conjunto de trabalho privado do processo diminuirão.
- As páginas na lista de espera agora podem serreaproveitado, ou seja, usado para outra coisa - como resolver falhas de página de qualquer processo no sistema ou usado pelo SuperFetch. No entanto...
- Se um processo que perdeu uma página na lista modificada ou em espera tentar acessá-la novamente antes que a página física tenha sido reaproveitada (ou seja, ela ainda tem seu conteúdo original), a falha da página será resolvida sem a leitura do disco. A página é simplesmente colocada de volta no conjunto de trabalho do processo e a entrada da tabela de páginas torna-se "válida". Este é um exemplo de falha de página "leve" ou "barata". Dizemos que as listas de espera e modificadas formam um cache de páginas em todo o sistema que provavelmente serão necessárias novamente em breve.
Se você não tiver um arquivo de paginação, as etapas 3 a 5 serão alteradas para:
As páginas ficam na lista modificada, pois não há onde escrever seu conteúdo.
As páginas ficam na lista modificada, pois não há onde escrever seu conteúdo.
As páginas ficam na lista modificada, pois não há onde escrever seu conteúdo.
A etapa 6 permanece a mesma, uma vez que as páginas da lista modificada podem ser devolvidas ao processo que as perdeu como uma falha de página "soft". Mas se isso não acontecer, as páginas ficarão na lista modificada até que o processo desaloque a memória virtual correspondente (talvez porque o processo termine).
Há outro uso de espaço de endereço virtual e de RAM, além da memória privada comprometida. Hámapeadoespaço de endereço virtual, para o qual o armazenamento de apoio é algum arquivo especificado em vez do arquivo de paginação. As páginas do vas mapeado que são paginadas são refletidas no uso de RAM, mas a memória mapeada não contribui para a cobrança de commit porque o arquivo mapeado fornece o armazenamento de apoio: Qualquer parte da região mapeada que não esteja na RAM é simplesmente mantida no arquivo mapeado. Outra diferença é que a maioria dos mapeamentos de arquivos pode ser compartilhada entre processos; uma página compartilhada que já está na memória para um processo pode ser adicionada a outro processo sem precisar ir ao disco novamente (outra falha de página flexível).
E aqui estánão paginávelvas, para o qual não há armazenamento de apoio porque é sempre residente na RAM. Isso contribui tanto para o uso relatado de RAM quanto para a "taxa de confirmação".
Parece que isso pode ser por causa da compactação. O que transforma a questão em: Por que o limite de commit não aumenta ou algo assim? Ou seja, qual é o ponto de compactação se isso não ajuda no uso de memória?
Não. Não tem nada a ver com compressão. A compactação de memória no Windows é feita como uma etapa intermediária, em páginas que de outra forma seriam gravadas no arquivo de paginação. Com efeito, permite aolista de páginas modificadausar menos RAM para conter mais coisas, com algum custo em tempo de CPU, mas com velocidade muito maior do que a E/S de arquivo de paginação (mesmo para um SSD). Como o limite de commit é calculado a partir detotalRAM + tamanho do arquivo de paginação, não uso de RAM + uso do arquivo de paginação, isso não afeta o limite de confirmação. O limite de confirmação não muda com a quantidade de RAM em uso ou para que é usada.
Quando a carga de commit é preenchida e o Windows começa a me pedir para fechar coisas, na maioria das vezes a memória física fica em torno de 60%. Isso parece terrivelmente ineficiente.
Não é que o Windows esteja sendo ineficiente. São os aplicativos que você está executando. Eles estão cometendo muito mais vas do que realmente usam.
A razão para todo o mecanismo de "cobrança de confirmação" e "limite de confirmação" é esta: quando chamo o VirtualAlloc, devo verificar o valor de retorno para ver se é diferente de zero. Se for zero, significa que minha tentativa de alocação falhou, provavelmente porque teria feito com que a cobrança de confirmação excedesse o limite de confirmação. Devo fazer algo razoável, como tentar comprometer menos ou sair do programa de forma limpa.
Se o VirtualAlloc retornou diferente de zero, ou seja, um endereço, isso me diz que o sistema fez uma garantia - um compromisso, por assim dizer - de que, independentemente de quantos bytes eu solicitei, começando nesse endereço,vai serdisponíveis se eu optar por acessá-los; que há algum lugar para colocar tudo - RAM ou arquivo de paginação. ou seja, não há razão para esperar qualquer tipo de falha no acesso a qualquer coisa naquela região. Isso é bom, porque não seria razoável esperar que eu verificasse "funcionou?" em todos os acessos à região alocada.
A analogia do “banco que empresta dinheiro”
É um pouco como um banco que oferece crédito, mas estritamente com base no dinheiro disponível. (É claro que não é assim que os bancos reais funcionam.)
Suponha que o banco comece com um milhão de dólares em dinheiro em mãos. As pessoas vão ao banco e pedem linhas de crédito em valores variados. Digamos que o banco me aprove para uma linha de crédito de US$ 100.000 (eu crio uma região privada comprometida); isso não significa que algum dinheiro tenha realmente saído do cofre. Se mais tarde eu realmente contrair um empréstimo de, digamos, US$ 20 mil (eu acesso um subconjunto da região), isso retirará dinheiro do banco.
Mas, quer eu contrate algum empréstimo ou não, o fato de ter sido aprovado para um máximo de US$ 100 mil significa que o banco só poderá aprovar posteriormente outras linhas de crédito no valor de US$ 900 mil, no total, para todos os seus clientes. O banco não aprovará crédito que exceda as suas reservas de caixa (ou seja, não aprovarácomprometer demaiseles), uma vez que isso significaria que o banco poderia ter que recusar um mutuário previamente aprovado quando ele aparecesse mais tarde com a intenção de contratardelesempréstimo. Isso seria muito mau porque o banco jáempenhadopermitir esses empréstimos e a reputação do banco despencaria.
Sim, isso é “ineficiente” em termos do uso desse dinheiro pelo banco. E quanto maior for a disparidade entre as linhas de crédito para as quais os clientes são aprovados e os montantes que efectivamente emprestam, menos eficiente será. Mas essa ineficiência não é culpa do banco; a “culpa” é dos clientes por solicitarem linhas de crédito tão altas, mas apenas contrairem pequenos empréstimos.
O modelo de negócios do banco é que ele simplesmente não pode recusar um mutuário previamente aprovado quando ele comparece para obter o empréstimo – fazê-lo seria “fatal” para o cliente. É por isso que o banco mantém um registo cuidadoso de quanto do fundo de empréstimo foi “comprometido”.
Suponho que expandir o arquivo de paginação, ou adicionar outro, seria como se o banco saísse e conseguisse mais dinheiro e o adicionasse ao fundo de empréstimo.
Se você deseja modelar memória mapeada e não paginável nesta analogia... não paginável é como um pequeno empréstimo que você deve contrair e manter ao abrir sua conta. (As estruturas não pagináveis que definem cada novo processo.) Memória mapeada é como trazer seu próprio dinheiro (o arquivo que está sendo mapeado) e depositá-lo no banco, depois retirar apenas partes dele de cada vez (paginá-lo). Por que não paginar tudo de uma vez? Não sei, talvez você não tenha espaço na carteira para todo esse dinheiro. :) Isso não afeta a capacidade de terceiros de pedir dinheiro emprestado porque o dinheiro que você depositou está em sua própria conta, não no fundo geral de empréstimos. Essa analogia começa a falhar aí, especialmente quando começamos a pensar em memória compartilhada, então não exagere.
De volta ao sistema operacional Windows: O fato de você ter grande parte da sua RAM "disponível" não tem nada a ver com a cobrança e o limite de confirmação. Se você estiver próximo do limite de commit, isso significa que o sistema operacional já foi confirmado - ou seja prometeu disponibilizarquando solicitado - tanto armazenamento. Não precisa estar totalmente em uso ainda para que o limite seja aplicado.
Posso deixar de inflar artificialmente meu arquivo de paginação para níveis que meu SSD carente de espaço está mal equipado para lidar, apenas para que eu possa realmente utilizar minha memória física de maneira eficaz? (Ou mesmo que não estivesse tão cheio. Ou seja, gostaria de evitar sugestões como "Faça X/Y/Z no seu arquivo de paginação".)
Bem, sinto muito, mas se você estiver atingindo o limite de commits, há apenas três coisas que você pode fazer:
- Aumente sua RAM.
- Aumente o tamanho do seu arquivo de paginação.
- Execute menos coisas de uma vez.
Opção 2: você pode colocar um segundo arquivo de paginação em um disco rígido. Se os aplicativos não estiverem realmente usando toda a memória comprometida - o que aparentemente não estão, já que você está vendo muita RAM livre - você não acessará muito esse arquivo de paginação, portanto, colocá-lo em um disco rígido não funcionará. prejudicar o desempenho. Se a lentidão de um disco rígido ainda incomoda você, outra opção é obter um segundo SSD pequeno e, portanto, barato e colocar seu segundo arquivo de paginação nele. O único "empecilho" seria um laptop sem maneira de adicionar uma segunda unidade "não removível". (O Windows não permitirá que você coloque arquivos de paginação em unidades removíveis, como qualquer coisa conectada por USB.)
Aqui estáoutra resposta que escreviisso explica as coisas de uma direção diferente.
ps: Você perguntou sobre o Windows 10, mas devo dizer que ele funciona da mesma maneira em todas as versões da família NT, desde o NT 3.1, e nas versões de pré-lançamento também. O que provavelmente mudou foi a configuração padrão do Windows para o tamanho do arquivo de paginação, de 1,5x ou 1x tamanho de RAM para muito menor. Acredito que isso foi um erro.