Problemas com LINUX

17 respostas
S

Olá... geralmente costumo achar todas as respostar para a programação por ai, e por isso nunca postei aqui... mas um problema está me intrigando...

Tenho um aplicativo de servidor java para conexões sockets.. meu intuito é fazer um jogo online em tempo real, de cartas. Até aí tudo bem...

Agora, fiz uma aplicação para testar exaustivamente novas conexões, para definir o limite de usuários que posso ter no servidor. Porém, o máximo de memória que a aplicação usa é por volta de 7mb, e depois da erro de memória. Mesmo usando "java -Xmx512m", tenho esse erro. Limpei o cache de memória do servidor e mesmo com mais de 512mb livres, não passa dos 7mb...

Uso um servidor VPS, alguém sabe se preciso de uma configuração extra?

E outra: Programei meu servidor para após a primeira conexão finalizar os sockets em 10 segundos caso não haja novas interações... Com isso e mais um delay entre cada nova requisição, acreditei que o programa de "exaustão" do servidor iria rodar infinitamente... porem mesmo limpando todas as referências dos sockets no código a memória continua a crescer.. Não entendo muito do GC, mas isso é esperado para tempos curtos assim ou não estou liberando corretamente a memória?

Aqui o que executa quando é feita a desconexão:
conected = null;
        progresso = null;
        USER_FB = null;
        nome = null;
        jogadas = null;
        user_admin = null;
        criador = null;
        paraenviar = null;
        serviceDisconect.shutdown();
        serviceDisconect = null;
        
        try{
            //out.writeUTF("disconect");
            in.close();
            out.close();
            socket.close();
            
            socket = null;
            in = null;
            out = null;
        }catch(IOException e){
            e.printStackTrace();
        }

Desculpem a enxurrada de dúvidas, mas sou daqueles "auto-didatas" que acabou não aprendendo as bases bem...

Abraços!

17 Respostas

S

Opa! boas novas… quanto ao aumento de memória consegui resolver… tinha uma lista dos sockets conectados e quando removia usava a lista.remove(socket), mas isso não é útil…

mudei para um for que acha o usuário e remove, e funcionou… fiz 3k+ conexões e a RAM permaneceu em 5%…

mas e quanto ao ponto de limite de RAM, continuo na mesma… alguem?

abraços.

R

Coloca a stack trace completa. Mesmo não esgotando a memória os sistemas operacionais, de maneira geral, limitam o número de sockets que podem ser abertos por processo. Talvez seja esse o caso. Mas para saber ao certo, somente com a stack trace do erro mesmo.

G

Por onde você está vendo isso? Pelo consumo de memória da máquina? Se for, essa informação não é tão precisa porque tem outras variáveis envolvidas. Por exemplo, o processo pode ter alocado memória mas ainda não usou, ou pode ter alguma coisa em swap, etc.

Se quer uma medição mais exata do uso de memória da JVM use o jconsole. É uma aplicação que vem com o Java SDK, ele se conecta ao processo que está em execução e faz o profiling.

Uma possibilidade muito bem lembrada!
Além da limitação por processo também podem esgotar as portas TCP da máquina no lado client. Já vi isso acontecer em alguns testes com jMeter, a recomendação deles nesse caso é fazer um teste com clientes distribuídos.

S

Bom… vamos la:

Iniciei o exaustor de conexões, ele cria um socket a cada 10ms, o servidor envia uma string para autenticar, mas o exaustor não responde e em 10 segundos a conexão é finalizada no servidor.

Rodei e ele conseguiu apenas 300 sockets em uma VPS de 1gb. Assim, os 300 sockets estavam abertos pois não tinham passado ainda os 10 segundos. Se eu colocar o tempo para cada socket no exaustor em 100ms, o servidor consegue eliminar recursos à tempo e o programa roda infinitamente. O erro:

Exception in thread "Thread-632" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:714) at pokercliente.User.enviadado(User.java:273) at pokercliente.User.lambda$new$0(User.java:72) at pokercliente.User$$Lambda$1/27206439.run(Unknown Source) at java.lang.Thread.run(Thread.java:745) out: autentica|851472743 out: autentica|918178931 Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:714) at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950) at java.util.concurrent.ThreadPoolExecutor.ensurePrestart(ThreadPoolExecutor.java:1587) at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:334) at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:533) at java.util.concurrent.Executors$DelegatedScheduledExecutorService.schedule(Executors.java:729) at pokercliente.User.<init>(User.java:94) at pokercliente.User_Admin$1.<init>(User_Admin.java:38) at pokercliente.User_Admin.addConexao(User_Admin.java:38) at pokercliente.PokerCliente.main(PokerCliente.java:34) Exception in thread "Thread-636" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:714) at pokercliente.User.enviadado(User.java:273) at pokercliente.User.lambda$new$0(User.java:72) at pokercliente.User$$Lambda$1/27206439.run(Unknown Source) at java.lang.Thread.run(Thread.java:745)

Ao iniciar a conexão é iniciado um novo thread, e após isso o servidor tenta enviar dados para o cliente através de um método “enviadado(string)”, que é executado em nova thread, por isso os erros acima.

Uso da memória: Reiniciei o VPS, antes de executar o exaustor:

free: 647mb

Logo antes de dar erro de memória:

free: 555mb

código que uso para rodar:

nohup java -Xmx512M -jar PokerCliente.jar

Duvido muito que o programa use os 512mb assim, mas estou pesquisando o JConsole para ver o consumo real…

Outra dúvida: O cache de memória do VPS enche muito rápido… Não entendo muito, mas tem como eu reservar uma memória do VPS para o java, impedindo que o sistema utilize esse espaço para cache? em VPS não tem como apagar o cache sem reiniciar o servidor =s…

Obrigado pela ajuda pessoal, logo posto o progresso com o JConsole!

Abraço!

S

Só mais uma: Alguém tem ideia de quanto um Socket consome de memória? Estou armazenando eles em uma ArrayList, e me parece que consome memória demais :o

Enfim, abraços!

R

slayer3, ao que tudo indica esse é um típico caso de memory leak mesmo, mas vamos por partes:

  • o parâmetro -Xmx na chamada da JVM indica qual será a memória máxima utilizada pelo heap. Assim, mesmo que você disponha de mais memória no SO, a JVM nunca utilizará um heap maior do que o especificado no parâmetro. Sendo assim, o erro OutOfMemoyError é disparado quando a JVM esgota a memória especificada em -Xmx, e não a memória do SO.

  • por menor que seja a memória consumida por um socket, mantê-los indefinidamente em um ArrayList ou estrutura de dados qualquer, fatalmente te levará a um esgotamento da memória, pois você deve presumir que a sua demanda aumentará infinitamente. Além disso, como eu já havia colocado, além da memória um socket consome outros recursos do SO: portas TCP, endereços de I/O, etc. E geralmente, esses recursos são bem mais limitados do que a memória em si.

Assim, a solução do seu problema passa, antes de mais nada, em utilizar um pool de threads:

http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html

e tenha em mente que você deve limitar a quantidade de usuários por servidor. Você deve prover um mecanismo para dropar requisições quando este limite for atingido.

S

Obrigado pela resposta… mas da uma olhada na memória usada… tu sabe me dizer por que meu programa não usa os 512mb teoricamente alocados para ele?

S

Implementei o ExecutorService, mas ele não reduz o uso da memória né? entendi que posso limitar o máximo de threads, mas não posso imaginar o jogo rodando com o máximo de 300 conexões simultâneas =o

R

slayer3:
Implementei o ExecutorService, mas ele não reduz o uso da memória né? entendi que posso limitar o máximo de threads, mas não posso imaginar o jogo rodando com o máximo de 300 conexões simultâneas =o

Colega, mas aí você esbarra nas Leis da Física … cedo ou tarde você precisa limitar o número de usuários, não há como haver memória ou recursos infinitos … você pode escalar a sua aplicação mais tarde, colocando mais memória ou comprando outro nó. Mas é fato que você precisa limitar os seus recursos.

S

Entendo isso… Mas acho 300 um numero muito baixo para 512 mb… E como no post anterior falei, pelos dados que passeis não da pra deduzir que a memória passada em Xmx não é utilizada toda? Existe outro fato que pode causar o erro da memoria?

R

O ideal é utilizar o VisualVM. Você pode encontrá-lo na pasta da jdk dentro de JDK_HOME/bin. Ele mostra como a memória do seu programa está sendo usado e quais objetos ocupam mais memória. É um começo;

S

Entendi…

Mas como pode a memoria do sistena baixar apenas ~100mb e o programa dar erro de falta de memoria se ele teria 512mb para rodar? Não entendo isso… Abraços

G

Esse erro não acontece por falta de memória Heap. Em alguns casos a JVM aloca memória diretamente do SO, memória separada do heap. Entre as situações em que isso acontece, está a inicialização de uma nova thread, que precisa de memória para a stack (parâmetros e variáveis locais). É justamente nesse ponto que acontece o erro:

at java.lang.Thread.start0(Native Method)  
        at java.lang.Thread.start(Thread.java:714)

Algumas coisas que se pode tentar para resolver:

  • Verificar se o SO tem limitação de memória total por processo (comando ulimit). A JVM poderia estar alocando o suficiente para a heap, mas na hora de criar stack para cada thread acaba esbarrando na limitação.
  • Tentar diminuir o tamanho da heap. Pode parecer estranho, mas diminuir o tamanho da memória heap poderia deixar mais memória nativa para as stacks.
  • Diminuir o tamanho da stack por thread. Aqui fala sobre isso:
    http://www.onkarjoshi.com/blog/209/using-xss-to-adjust-java-default-thread-stack-size-to-save-memory-and-prevent-stackoverflowerror/ - no exemplo do site houve necessidade de aumentar devido a StackOverflowError. No seu caso precisaria reduzir para ser capaz de executar mais threads, comece com um valor baixo como -Xss64kb

Acho que a preocupação dele não é ter uma limitação, mas essa limitação ser muito baixa. Também acho que 300 conexões é pouco para um serviço desse tipo, daria para ir além, talvez apenas com alguns detalhes na aplicação ou na configuração da VM.

S

Muuito obrigado, isso que eu prochrava… Vou testar as propostas e dou retorno em breve… No mais, usar um VPS limita muito isso será? Precisarei de um servidor dedicado será? Perguntas off mas qie talvez alguém saiba…

S

O uso de Xss não mudou nada, tive que usar 126k pois dizia que o mínimo era 72…

Executeu “ulimit -a” e encontrei alguns valores estranhos que gostaria que alguem me explicasse:

stack size (kbytes, -s) 10240
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited

Continuo com os mesmos 300 e poucos sockets =s

S

Fiz o teste pelo meu note. Tenho 2gb de ram.

Executei o exaustor com intervalo de 10ms para ver que limite dava, e atingiu 1,4k de conexões com ~230mb usados. Modifiquei o -Xmx para 1024m e não mudou, depois desci ele para 126m e não mudou também… deram os mesmos resultados… Sou novato nisso, mas me parece diferente de tudo que lí até agora…

S

Ok. Depois de muito pesquisar encontrei algumas boas respostar por ai também, e vou postar aqui para que outros possam usufruir.

Primeiro: Usar muitas threads parece muito ruim. Não crie, jamais, uma thread para cada usuário se você espera receber muitos usuários em sockets, pois criar uma thread consome memória do SO, e cada SO tem uma limitação, mas acredito que não vá atingir o nível desejado. Para tanto, existe o SocketChannel.

Pesquisei muito sobre e não encontrei muita coisa, mas finalmente achei um size que explica como funciona e o que precisa. http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=1

Sim, você precisará reescrever boa parte do código e aprender uns bons conceitos, mas funciona e no fim vai ver que não é tão difícil quanto pensava…

Se alguém tiver mais alguma contribuição, será bem vinda…

No mais, abraços e obrigado pela imensa ajuda!

Criado 26 de junho de 2014
Ultima resposta 26 de jun. de 2014
Respostas 17
Participantes 3