Como posso limitar a quantidade de memória disponível para um processo?

11

Estou desenvolvendo um programa em andamento; ocasionalmente, acaba alocando quantidades muito grandes de memória (>> 10G em uma máquina com 8G de memória física), fazendo com que o sistema deixe de responder. Quero limitar a quantidade de memória que o processo pode alocar. A maneira usual de fazer isso é:

ulimit -m 4000000 ; ./myprogram

... o que deve matar o meu programa se ele tentar usar mais de 4 GB de memória.

No OS X El Capitan, isso parece não ter efeito; even ulimit -m 1(limitar todos os programas a apenas 1kB de memória!) é ineficaz.

Como posso definir um limite superior na memória disponível para um processo específico?

cpcallen
fonte
Quando você diz Da maneira usual como eu faria isso , o que você quer dizer? Mais especificamente, quando você costuma fazer isso? E quando você faz isso, você quer dizer no Mac OS X El Capitan ou em outro ambiente? Você pode dar um exemplo de quando você o usou com sucesso? Basicamente, estou apenas tentando esclarecer se esse processo funciona normalmente para você, mas não está funcionando para esse programa em particular? Ou não está funcionando para você, mas você acha que deveria?
Monomeeth
1
Por "da maneira usual em que eu faria isso", quero dizer da maneira que faria em outros sabores do unix. Observo que acabei de descobrir que ulimit -mnão funciona mais no Linux (> 2.4.30), embora ulimit -vainda funcione conforme o esperado. (Como ulimit -m, ulimit -vtambém parece ter nenhum efeito sobre o OS X.)
cpcallen
Parece que você tem um vazamento de memória no programa que está escrevendo e precisa fazer uma melhor coleta de lixo. Você já leu sobre gerenciamento de memória?
Todd Dabney
1
Não, não há vazamento de memória - apenas uma pesquisa gráfica de um espaço de estado potencialmente muito grande. Eu poderia adicionar código para abortar a pesquisa se as várias estruturas internas ficarem muito grandes, mas esperava poder fazer a mesma coisa (impedir que a máquina se envolva com entradas grandes) com um liner de shell.
precisa

Respostas:

1

Existem duas abordagens para restringir o uso de memória: Ex post facto e preventivo. Ou seja, você pode tentar matar o seu programa depois que ele ficou muito grande ou você pode programá-lo para não ficar muito grande em primeiro lugar.

Se você insistir na abordagem ex post facto, poderá usar o seguinte script Bash. Esse script primeiro encontra a quantidade de memória (conforme definida por "tamanho do conjunto residente") que o processo com pid processid está usando, filtra todos os dados não numéricos usando grep e salva a quantidade como variável n. O script verifica se n é maior que o x especificado. Se for, o processo com pid processid é interrompido.

Observe:

  1. Você deve substituir <pid>pela identificação do processo do seu programa.
  2. Você deve substituir <x>pelo rss = "tamanho do conjunto de residentes" (ou seja, tamanho da memória real) que não deseja que o programa exceda.

n=$(ps -<pid> -o rss | grep '[0-9]') if [ $n -gt <x> ]; then kill -9 <pid>; fi

Se você deseja que isso seja executado a cada y segundos, inclua-o em um loop e peça para aguardar y segundos após cada iteração. Você também pode escrever um comando semelhante usando top. Seu ponto de partida seria top -l 1|grep "<pid>"|awk '{print $10}'.

A resposta de @ kenorb me ajudou com meu script


Embora eu acredite que responda à pergunta, a longo prazo, acredito que é melhor o design da programação adotar uma abordagem preventiva usando a alocação manual de memória.

Em primeiro lugar, você tem certeza de que o uso da memória é realmente um problema? A documentação do Go declara:

O alocador de memória Go reserva uma grande região de memória virtual como uma arena para alocações. Essa memória virtual é local para o processo Go específico; a reserva não priva outros processos de memória.

Se você ainda acha que tem algum problema, recomendamos que você gerencie manualmente sua memória, como é feito na linguagem de programação C. Como go está escrito em C, suspeitei que haveria maneiras de entrar no gerenciamento / alocações de memória C e, de fato, existem. Veja este repositório do github que,

permite que você faça o gerenciamento manual de memória através do alocador C padrão para o seu sistema. É um invólucro fino sobre malloc, calloc e isento de. Consulte man malloc para obter detalhes sobre essas funções para o seu sistema. Esta biblioteca usa cgo.

O caso de uso é fornecido como:

Por que você quer isso?

Quando um programa está causando pressão na memória ou o sistema está ficando sem memória, pode ser útil controlar manualmente as alocações e desalocações de memória. O Go pode ajudá-lo a controlar alocações, mas não é possível desalocar explicitamente dados desnecessários.

Parece uma solução melhor a longo prazo.

Se você quiser aprender mais sobre C (incluindo gerenciamento de memória), a linguagem de programação C é a referência padrão.

Evan Rosica
fonte
Estou certo de que o uso da memória é realmente um problema. Quando o programa inesperadamente cresceu para> 2 vezes o tamanho da memória física, a máquina tornou-se quase totalmente sem resposta devido à interrupção da página. Demorou cerca de uma hora entre quando eu pressionei ^ C e quando o macOS retomou a resposta aos cliques do mouse; Nesse meio tempo, a única evidência de que não estava congelada era o barulho silencioso do disco rígido correndo rapidamente.
Cspcallen
Essa é provavelmente uma solução razoável na prática, mas, diferentemente dos sistemas operacionais UNIX (não Darwin), depende de poder alocar memória suficiente em tempo hábil para executar com êxito o comando kill. Eu realmente prefiro ter limites de tamanho de processo aplicados pelo kernel.
precisa saber é o seguinte
@cpcallen fazendo algumas pesquisas, parece que o parâmetro "-m para ulimit não tem efeito em sistemas Linux com versões de kernel mais recentes que 2.4.30". Parece que o OSX também percebeu essa mudança. Experimente a opção -v para limitar o espaço de endereço
Evan Rosica