2015-10-03 7 views
20

Próbuję zrozumieć, kiedy pamięć przydzielona poza stertą Rubin zostaje zwrócona do systemu operacyjnego. Rozumiem, że Ruby nigdy nie zwraca pamięci przydzielonej do jego sterty, ale nadal nie jestem pewien co do zachowania pamięci sterty. tj. te obiekty, które nie mieszczą się w 40-bajtowej RAWALNOŚCI.Dlaczego ten program Ruby nie zwraca pamięci sterty do systemu operacyjnego?

Rozważmy następujący program, który przydziela niektóre duże ciągi, a następnie wymusza większy GC.

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') 

Uruchamiając ten program za pomocą Rubiego 2.2.3 na MRI, generuje następujący wynik. Po wymuszonym głównym GC wielkość sterty jest zgodna z oczekiwaniami, ale RSS nie zmniejszył się znacząco.

------------------- 
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 

Porównaj te wyniki z następującymi wynikami, gdy przydzielimy jeden duży obiekt zamiast wielu mniejszych obiektów.

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 

Uwaga na ostatnią wartość RSS. Wydaje się, że uwolniliśmy całą pamięć, którą przydzieliliśmy na duży ciąg.

Nie jestem pewien, dlaczego drugi przykład zwalnia pamięć, ale pierwszy przykład nie jest taki, ponieważ oba przydzielają pamięć do stosu Ruby. To jest jeden numer reference, który może dostarczyć wyjaśnienia, ale byłbym zainteresowany wyjaśnieniami od innych.

Zwolnienie pamięci z powrotem do jądra również kosztuje. Pamięć przestrzeni użytkownika Przydzielacze mogą przechowywać tę pamięć (prywatnie) w nadziei, że będzie ona mogła zostać ponownie użyta w tym samym procesie i nie zostanie zwrócona do jądra, aby można ją było wykorzystać w innych procesach.

+1

Zapisywanie do tego wątku. Jestem * bardzo * zainteresowany również tym. – dimitarvp

+1

Podstawowa różnica jest w pierwszym przykładzie, gdzie powstają 600k * nowe * obiekty, w drugim tylko jeden. Chociaż całkowity rozmiar danych * referencyjnych * jest taki sam, pierwszy przykład wymaga 600 tysięcy razy więcej slotów dla obiektów referencyjnych (które prawdopodobnie nigdy lub później nie zostaną zregenerowane do systemu operacyjnego). – joanbm

+2

Proponuję zapoznać się z [artykułem] (http://www.sitepoint.com/ruby-uses-memory/) i powiązać [wyjaśnienie] (http://rocket-science.ru/hacking/2013/12/17/ruby-memory-pułapek /) z 'RVALUE's. Nie jestem pewien, czy są one poprawne, tylko Koichi znany jako ko1 może wiedzieć. Lub jakiś silnie zdeterminowany entuzjasta, analizujący źródła Rubiego. – joanbm

Odpowiedz

1

@joanbm ma tutaj bardzo dobry punkt. Jego odniesienie do GC Rubiego stopniowo zwalnia pamięć, więc kiedy wykonujesz GC na 1 dużym kawałku pamięci wskazanym przez 1 referencję, to uwalnia to wszystko, ale gdy jest dużo odniesień, GC zwolni pamięć w mniejszej chuncks.

Kilka połączeń z numerem GC.start spowoduje zwolnienie pamięci w pierwszym przykładzie.


Oto 2 artykuły Orther kopać głębiej:

Powiązane problemy