2015-07-07 3 views
26

Quando rimuovo il ciclo for, viene visualizzato un errore OutOfMemoryError. Quando uso per loop non ottengo errore. Qualcuno può aiutarmi a capire questo comportamento?L'aggiunta del ciclo For impedisce l'errore OutOfMemoryError

public class JavaMemoryPuzzlePolite { 
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6); 

    public void f() { 
     { 
      System.out.println(dataSize); 
      byte[] data = new byte[dataSize]; 
     } 
     for (int i = 0; i < 1; i++) { 
      System.out.println("Please be so kind and release memory"); 
     } 
     System.out.println(dataSize); 
     byte[] data2 = new byte[dataSize]; 
    } 

    public static void main(String[] args) { 
     JavaMemoryPuzzlePolite jmp = new JavaMemoryPuzzlePolite(); 
     jmp.f(); 
    } 
} 
+0

Vedere anche http://stackoverflow.com/a/938080/545127 – Raedwald

risposta

28

Procedimento f() viene eseguito nel telaio interpretato. I frame interpretati si comportano in modo diverso rispetto ai frame compilati con JIT. Ecco come appare nel pseudocodice senza il ciclo for:

1. Allocate dataSize bytes of memory 
2. Store it into variable slot #1 
3. Allocate dataSize bytes of memory 
4. Store it into variable slot #1 

Così si ha la OutOfMemoryError sul gradino # 3 come il vecchio byte[] matrice risiede ancora in 1 variabile #. Tuttavia l'aggiunta del ciclo for (in realtà l'aggiunta di una variabile i) fa cosa diversa:

1. Allocate dataSize bytes of memory 
2. Store it into variable slot #1 
3. Store 0 to slot #1 (thus byte[] array is now eligible for GC) 
4. Do the for loop 
5. Allocate dataSize bytes of memory 
6. Store it into variable slot #2 

Qui quando si assegna il nuovo array al passo # 5, il primo array può già essere garbage collection.

Si noti che il compilatore JIT può comportarsi in modo più intelligente e scollegare il primo array dalla variabile non più utilizzato (nel caso specifico non lo allocherà affatto).

Si noti inoltre che nel caso specifico il risultato dipende dal compilatore java. ECJ (compilatore Eclipse) è abbastanza intelligente da non memorizzare il primo array in una variabile in quanto non viene utilizzato. Quindi non avrai OutOfMemoryError nella classe compilata dall'ECJ anche senza il ciclo for.

Per ulteriori dettagli è possibile esaminare l'output di smontaggio del codice by fornito dall'utilità javap e vedere come vengono riutilizzati gli slot delle variabili.

+0

puoi spiegare il passaggio 3 un po 'di più? '3. Memorizza 0 nello slot # 1 (quindi l'array byte [] ora è idoneo per GC) ' – asgs

+3

@asgs, ogni variabile locale ha il suo slot che viene assegnato dal compilatore javac. Il 'data' è assegnato allo slot # 1. Quindi il blocco finisce, quindi lo slot può essere riutilizzato e anche la variabile successiva avrà lo slot # 1. Con for loop la variabile successiva è 'i'. Quindi 'int i = 0' è tradotto in' iconst_0/istore_1' che è "Memorizza 0 nello slot # 1" nella mia risposta. L'interprete non si cura se è in realtà una nuova variabile, sostituisce semplicemente il valore precedente, quindi diventa senza riferimento. –

+0

@TagirValeev perfetto, grazie! Non 'iconst_0/istore_1' significa" Memorizza 1 nello slot # 0 "piuttosto che" Memorizza 0 nello slot # 1 ". – asgs