2014-12-09 6 views
7

Ho riscontrato un bug in cui una delle nostre applicazioni server stava usando sempre di più memoria ogni secondo più o meno e sono riuscito a filtrare un breve esempio che mostra ancora che il comportamento:Files.getLastModifiedTime() perde memoria?

public class TestGetLastModifiedTime { 
    private static final Path PATH = Paths.get("D:\\test.txt"); 
    private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1); 

    public static void main(String[] args) { 
     SCHEDULER.scheduleAtFixedRate(() -> getLastModifiedTime(), 0, 1, TimeUnit.SECONDS); 
    } 

    private static void getLastModifiedTime() { 
     try { 
      FileTime lastModifiedTime = Files.getLastModifiedTime(PATH); 
     } catch (IOException ex) { 
      throw new UncheckedIOException(ex); 
     } 
    } 
} 

esecuzione su Windows 8.1 e Java 8u20.

Tramite VisualVM ho osservato che la dimensione massima dell'heap non aumenta e che lo stesso heap continua ad aumentare. Allo stesso tempo, osservo in Task Manager di Windows che il processo spawn di java.exe continua a utilizzare (riservando) più memoria ogni secondo.

La parte interessante è che quando eseguire GC dall'interno VisualVM, tutta la memoria mucchio utilizzata viene resettato praticamente a zero e la memoria utilizzata del processo java.exe non si restringe, come previsto, in quanto è considerato riservato.

Tuttavia, dopo che il GC è stato eseguito, l'utilizzo della memoria aumenta ancora ogni secondo, mentre ora c'è sicuramente abbastanza spazio libero.

Anche Metaspace non è interessato.

Per me questo odora davvero e sembra che la JVM abbia una perdita di memoria.

Qualcuno può aiutarmi e spiegare cosa sta succedendo qui?

+0

lo prendo non viene generata un'eccezione? – fge

+0

Ecco come funziona la garbage collection. Il programma continua ad allocare memoria che alla fine si trasforma in spazzatura. Quando non può allocare più memoria dal pool gratuito, raccoglie la spazzatura. Ci sarebbe un problema solo se l'heap non diminuisse * dopo * GC (se attivato automaticamente dalla JVM o manualmente). – kdgregory

+0

@fge Non vengono emesse eccezioni – skiwi

risposta

7

non considero come perdita per le seguenti ragioni:

  1. si parla che quando si attiva gc, l'utilizzo della memoria cade di nuovo a difetto. Non è così che funziona una perdita. In caso di perdita, tali oggetti sono facilmente raggiungibili e anche dopo raccolte di dati inutili ripetitivi, la dimensione dell'heap non diminuisce in modo significativo.
  2. L'accumulo di heap non significa una perdita. Può anche significare che troppi oggetti vengono creati. Quale è normale e perfettamente bene. E nel tuo caso, come viene chiamato in un ciclo. sì
  3. Sulla mia macchina, java -Xmx20M TestGetLastModifiedTime ha funzionato perfettamente per 10 minuti prima che avessi ucciso il processo. Se ci fosse una perdita, alla fine avrebbe gettato OutOfMemoryError o ha troppe ripetizioni gc
  4. Se si osserva in visualvm, il consumo di memoria è salito tra 2mb e 2.8mb. Che quasi nessun sospetto di perdita. Inoltre, questo consumo di memoria è previsto, poiché Files.getLastModifiedTime(PATH), ExecutorService crea internamente piccoli oggetti di utilità qua e là. Quindi mi sembra perfetto.

Il comportamento sulla mia macchina:

enter image description here

del punto, che il gestore di Windows sta mostrando utilizzo heap più alto. È abbastanza possibile JVM può riservare spazio se necessario. Potrebbe essere certamente corretto aumentare l'utilizzo dell'heap rispetto a gc. Il che ha perfettamente senso (perché dovresti subire l'austerità quando sei ricco?).

Ecco perché strumenti come l'osservazione di Windows Manager/free -m ecc. Non danno giustamente l'osservazione di cosa sta accadendo internamente.Per ottenere stime rapide, si potrebbe desiderare di fare:

jstat -gccapacity 9043 | tail -n 1 | awk '{ print $4, $5, $6, $10 }' | python -c "import sys; nums = [x.split(' ') for x in sys.stdin.readlines()]; print(str(sum([float(x) for x in nums[0]])/1024.0) + ' mb');" 
# change the pid from 9043 to your jvm process id. 
#jvm process id can be known by running `jcmd` command (available as part of jdk) 

Che ancora ti dà una stima migliore rispetto free -m/Manager di Windows

+0

Stavo pensando più a una fuga di risorse native, dove il codice C sottostante nella JVM avrebbe perso memoria quando chiamava 'Files.getLastModifiedTime()', forse non ero abbastanza chiaro nella mia domanda. – skiwi

+1

Durante il monitoraggio con 'JVisuaVM', suppongo che il sovraccarico di JMX che invia costantemente aggiornamenti sull'utilizzo della memoria è molto più grande del consumo di memoria di una chiamata' Files.getLastModifiedTime' al secondo ... – Holger

+0

@skiwi Sono su Ubuntu. Sembra ok sul mio Potrebbe essere un problema su Windows. Tali cose possono avere prestazioni diverse su piattaforme diverse. Quindi potresti ancora voler confermare te stesso applicando una logica simile in caso di Windows – Jatin