2015-10-03 4 views
20

Sto cercando di capire quando la memoria allocata dall'heap di Ruby viene restituita al sistema operativo. Capisco che Ruby non restituisce mai la memoria allocata al suo heap, ma non sono ancora sicuro del comportamento della memoria heap. cioè quegli oggetti che non rientrano in un valore RVALUE di 40 byte.Perché questo programma Ruby non restituisce memoria heap al sistema operativo?

Considerare il seguente programma che alloca alcune stringhe grandi e quindi impone un GC importante.

require 'objspace' 

STRING_SIZE = 250 

def print_stats(msg) 
    puts '-------------------' 
    puts msg 
    puts '-------------------' 
    puts "RSS: #{`ps -eo rss,pid | grep #{Process.pid} | grep -v grep | awk '{ print $1,"KB";}'`}" 
    puts "HEAP SIZE: #{(GC.stat[:heap_sorted_length] * 408 * 40)/1024} KB" 
    puts "SIZE OF ALL OBJECTS: #{ObjectSpace.memsize_of_all/1024} KB" 
end 

def run 
    print_stats('START WORK') 
    @data=[] 
    600_000.times do 
    @data << " " * STRING_SIZE 
    end 
    print_stats('END WORK') 
    @data=nil 
end 

run 
GC.start 
print_stats('AFTER FORCED MAJOR GC') 

Esecuzione di questo programma con Ruby 2.2.3 su MRI produce il seguente output. Dopo un GC importante forzato, la dimensione dell'heap è come previsto ma l'RSS non è diminuito in modo significativo.

------------------- 
START WORK 
------------------- 
RSS: 7036 KB 
HEAP SIZE: 1195 KB 
SIZE OF ALL OBJECTS: 3172 KB 
------------------- 
END WORK 
------------------- 
RSS: 205660 KB 
HEAP SIZE: 35046 KB 
SIZE OF ALL OBJECTS: 178423 KB 
------------------- 
AFTER FORCED MAJOR GC 
------------------- 
RSS: 164492 KB 
HEAP SIZE: 35046 KB 
SIZE OF ALL OBJECTS: 2484 KB 

Confrontare questi risultati con i seguenti risultati quando allociamo un oggetto di grandi dimensioni invece di molti oggetti più piccoli.

def run 
    print_stats('START WORK') 
    @data = " " * STRING_SIZE * 600_000 
    print_stats('END WORK') 
    @data=nil 
end 

------------------- 
START WORK 
------------------- 
RSS: 7072 KB 
HEAP SIZE: 1195 KB 
SIZE OF ALL OBJECTS: 3170 KB 
------------------- 
END WORK 
------------------- 
RSS: 153584 KB 
HEAP SIZE: 1195 KB 
SIZE OF ALL OBJECTS: 149064 KB 
------------------- 
AFTER FORCED MAJOR GC 
------------------- 
RSS: 7096 KB 
HEAP SIZE: 1195 KB 
SIZE OF ALL OBJECTS: 2483 KB 

Prendere nota del valore RSS finale. Sembra che abbiamo liberato tutta la memoria che abbiamo assegnato per la grande stringa.

Non sono sicuro del motivo per cui il secondo esempio rilascia la memoria, ma il primo esempio non esegue l'allocazione della memoria dall'heap di Ruby. Questo è uno reference che potrebbe fornire una spiegazione, ma sarei interessato alle spiegazioni degli altri.

Anche il rilascio di memoria sul kernel ha un costo. Gli allocatori di memoria utente possono rimanere su quella memoria (privatamente) nella speranza che possa essere riutilizzato nello stesso processo e non restituirlo al kernel per l'utilizzo di in altri processi.

+1

Iscrizione a questo thread. Sono * molto * interessato anche a questo. – dimitarvp

+1

La differenza fondamentale è nel primo esempio, dove vengono creati 600k * nuovi * oggetti, nel secondo solo uno. Sebbene la dimensione totale dei dati * referenziati * sia la stessa, il primo esempio richiede 600 mila volte più slot per oggetti referenziati (che probabilmente non saranno mai o molto più tardi recuperati nel sistema operativo). – joanbm

+2

Suggerirei di seguire [articolo] (http://www.sitepoint.com/ruby-uses-memory/) e collegato [spiegazione] (http://rocket-science.ru/hacking/2013/12/17/ruby-memory-pitfalls /) di 'RVALUE's. Non sono sicuro che siano del tutto corretti, solo Koichi, noto anche come ko1, potrebbe saperlo. O qualche appassionato fortemente determinato, analizzando le fonti di Ruby. – joanbm

risposta

1

@joanbm ha un ottimo punto qui. Il suo riferimento article explains this pretty well:

Il GC di Ruby rilascia gradualmente la memoria, quindi quando si esegue GC su 1 grande blocco di memoria puntato da 1 riferimento rilascia tutto, ma quando vi sono molti riferimenti, il GC rilascerà la memoria in caratteri più piccoli chuncks.

Diverse chiamate a GC.start rilasceranno sempre più memoria nel primo esempio.


Qui ci sono 2 articoli orther scavare più a fondo: