Por que a Sun JVM continua a consumir cada vez mais memória RSS mesmo quando os tamanhos heap, etc estão estáveis?

No ano passado, obtive grandes melhorias no uso de heap Java do meu aplicativo - uma redução sólida de 66%. Em busca disso, tenho monitorado várias métricas, como tamanho de heap Java, cpu, Java não-heap, etc. via SNMP.

Recentemente, tenho monitorado o quanto de memória real (RSS, conjunto residente) pela JVM e estou um pouco surpreso. A memória real consumida pela JVM parece totalmente independente do tamanho de heap dos meus aplicativos, não heap, espaço eden, contagem de threads, etc.

Tamanho do heap conforme medido pelo Java SNMP Gráfico Usado do Java Heap http://lanai.dietpizza.ch/images/jvm-heap-used.png

Memória real em KB. (Por exemplo: 1 MB de KB = 1 GB) Gráfico Usado do Java Heap http://lanai.dietpizza.ch/images/jvm-rss.png

(Os três mergulhos no gráfico de heap correspondem às atualizações / reinicializações do aplicativo.)

Este é um problema para mim porque toda a memória extra que a JVM está consumindo está "roubando" a memória que poderia ser usada pelo sistema operacional para o armazenamento em cache de arquivos. Na verdade, quando o valor de RSS atinge ~ 2,5-3 GB, começo a ver tempos de resposta mais lentos e maior utilização da CPU do meu aplicativo, principalmente para esperas de IO. Como algum ponto de paginação para a partição swap entra em ação. Isso é tudo muito indesejável.

Então, minhas perguntas:

Por que isso está acontecendo? O que está acontecendo"sob o capô"?O que posso fazer para manter o consumo de memória real da JVM sob controle?

Os detalhes sangrentos:

RHEL4 de 64 bits (Linux - 2.6.9-78.0.5.ELp # 1 SMP qua 24 set ... 2008 x86_64 ... GNU / Linux)Java 6 (build 1.6.0_07-b06)Tomcat 6Aplicativo (streaming de vídeo HTTP sob demanda)Alta E / S via java.nio FileChannelsCentenas a milhares de fiosBaixo uso de banco de dadosPrimavera, hibernação

Parâmetros relevantes da JVM:

-Xms128m  
-Xmx640m  
-XX:+UseConcMarkSweepGC  
-XX:+AlwaysActAsServerClassMachine  
-XX:+CMSIncrementalMode    

-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps  
-XX:+PrintGCApplicationStoppedTime  
-XX:+CMSLoopWarn  
-XX:+HeapDumpOnOutOfMemoryError 

Como eu meço o RSS:

ps x -o command,rss | grep java | grep latest | cut -b 17-

Isso vai para um arquivo de texto e é lido em um banco de dados RRD do meu sistema de monitoramento em intervalos regulares. Observe que o ps gera Kilo Bytes.

O problema e a soluçãos:

Enquanto no final foiATRARASresposta que acabou por ser correta,kdgregory que me orientou para o caminho correto do diagnóstico com o uso depmap. (Vá votar as duas respostas!) Aqui está o que estava acontecendo:

Coisas que eu tenho certeza:

Meu aplicativo registra e exibe dados comJRobin 1.4, algo que eu codifiquei no meu aplicativo há mais de três anos.A instância mais ocupada do aplicativo cria atualmenteMais de 1000 novos arquivos de banco de dados JRobin (com cerca de 1,3 MB cada) dentro de uma hora após o início~ 100 + a cada dia após o start-upO aplicativo atualiza esses objetos de banco de dados JRobin uma vez a cada 15 segundos, se houver algo para escrever.Na configuração padrão JRobin:usa umjava.nioBack-end de acesso a arquivos baseado em Estes mapas de back-endMappedByteBuffers aos arquivos em si.uma vez a cada cinco minutos, um encadeamento do daemon do JRobinMappedByteBuffer.force() em cada banco de dados subjacente MBB JRobinpmap listados:6500 mapeamentos5500 dos quais eram arquivos de banco de dados JMBobin de 1,3 MB, o que significa ~ 7.1GB

Esse último ponto foi meu"Eureka!" momento.

Minhas ações corretivas:

Considere atualizar para o mais recente JRobinLite 1.5.2, que aparentemente é melhorImplemente o tratamento adequado de recursos nos bancos de dados JRobin. No momento, uma vez que meu aplicativo cria um banco de dados e nunca o despeja depois que o banco de dados não é mais usado ativamente.Experimente mover oMappedByteBuffer.force() para eventos de atualização de banco de dados e não um cronômetro periódico. O problema desaparecerá magicamente?Imediatamente, altere o backend JRobin para a implementação java.io - uma mudança de linha de linha. Isso será mais lento, mas possivelmente não é um problema. Aqui está um gráfico mostrando o impacto imediato dessa mudança.

Java RSS memória utilizada gráfico http://lanai.dietpizza.ch/images/stackoverflow-rss-problem-fixed.png

Perguntas que eu posso ou não ter tempo para descobrir:

O que está acontecendo dentro da JVM comMappedByteBuffer.force()? Se nada mudou, ele ainda escreve o arquivo inteiro? Parte do arquivo? Ele carrega primeiro?Existe uma certa quantidade de MBB sempre em RSS em todos os momentos? (RSS foi aproximadamente metade do total de tamanhos MBB alocados. Coincidência? Eu suspeito que não.)Se eu mover oMappedByteBuffer.force() para eventos de atualização do banco de dados, e não um cronômetro periódico, o problema desaparecerá magicamente?Por que o declive RSS era tão regular? Ele não está correlacionado a nenhuma das métricas de carga do aplicativo.

questionAnswers(4)

yourAnswerToTheQuestion