SlideShare une entreprise Scribd logo
1  sur  47
Télécharger pour lire hors ligne
Память Ruby изнутри
            Василий Федосеев
Долгоживущие процессы


Проблема утечек памяти и стейтов
Нельзя просто перезапустить процесс, как это делает passenger
Управление памятью
Объекты в MRI

Всё - это объекты
Их очень много
Живут в куче (heap) в фиксированных слотах
 sizeof(RVALUE) = 40 (обычно)
ObjectSpace

         _id2ref
         count_objects
         each_object
         garbage_collect
         define_finalizer / undefine_finalizer
Use the Source, Luke!
ObjectSpace




RVALUE
                                    ObjectSpace



          Heap


RVALUE
                    ...
         freelist




RVALUE
                      Heap
                             Heap




 free

RVALUE

 free

 free

RVALUE
Параметры GC
      RUBY_GC_MALLOC_LIMIT
       По умолчанию 8 Мб
      RUBY_HEAP_MIN_SLOTS
       10k
       1.9.2 стартует с 17k объектов, 2.0.0 с 15k
      RUBY_FREE_MIN
       4096
Однако

2.0.0dev :001 > ObjectSpace.each_object(Hash){}
 => 118
2.0.0dev :002 > ObjectSpace.each_object(Fixnum){}
 => 0
2.0.0dev :003 > ObjectSpace.each_object(Symbol){}
 => 0
Не всё - объекты
Object ID
    2.0.0dev :001 > 0.object_id
     => 1
    2.0.0dev :002 > 1.object_id
     => 3
    2.0.0dev :003 > :a.object_id
     => 468808
    2.0.0dev :004 > "a".object_id
     => 70199055954380
    2.0.0dev :005 > true.object_id
     => 20
    2.0.0dev :006 > false.object_id
     => 0
    2.0.0dev :007 > nil.object_id
     => 8
Object ID
                    Fixnum flag


                                 RUBY_Qfalse   =   0x00,
   101010101011 1                RUBY_Qtrue    =   0x14,
                                 RUBY_Qnil     =   0x08,
                                 RUBY_Qundef   =   0x34,

   Symbol id 1100                RUBY_IMMEDIATE_MASK
                                 RUBY_FIXNUM_FLAG
                                                           =
                                                           =
                                                               0x07,
                                                               0x01,
                                 RUBY_FLONUM_MASK          =   0x03,
                                 RUBY_FLONUM_FLAG          =   0x02,
                                 RUBY_SYMBOL_FLAG          =   0x0c,
   RVALUE ptr 000                RUBY_SPECIAL_SHIFT        =   8
enum ruby_value_type {



RBasic
                          RUBY_T_NONE   = 0x00,

                           RUBY_T_OBJECT =     0x01,
                           RUBY_T_CLASS =      0x02,
                           RUBY_T_MODULE =     0x03,
                           RUBY_T_FLOAT =      0x04,
                           RUBY_T_STRING =     0x05,
                           RUBY_T_REGEXP =     0x06,
                           RUBY_T_ARRAY =      0x07,
                           RUBY_T_HASH    =    0x08,
                           RUBY_T_STRUCT =     0x09,
                           RUBY_T_BIGNUM =     0x0a,
                           RUBY_T_FILE    =    0x0b,
   struct RBasic {         RUBY_T_DATA    =    0x0c,
       VALUE flags;        RUBY_T_MATCH =      0x0d,
                           RUBY_T_COMPLEX      = 0x0e,
       VALUE klass;        RUBY_T_RATIONAL     = 0x0f,
   };                      RUBY_T_NIL      =   0x11,
                           RUBY_T_TRUE     =   0x12,
                           RUBY_T_FALSE    =   0x13,
                           RUBY_T_SYMBOL   =   0x14,
                           RUBY_T_FIXNUM   =   0x15,

                           RUBY_T_UNDEF    =   0x1b,
                           RUBY_T_NODE     =   0x1c,
                           RUBY_T_ICLASS   =   0x1d,
                           RUBY_T_ZOMBIE   =   0x1e,

                           RUBY_T_MASK     = 0x1f
                      };
RObject
 #define ROBJECT_EMBED_LEN_MAX 3
 struct RObject {

    struct RBasic {
        VALUE flags;
        VALUE klass;
    };

    union {
    ! struct {
 !        long numiv;
    !     VALUE *ivptr;
          struct st_table *iv_index_tbl;
                                           ivars[numiv]
    ! } heap;
 ! VALUE ary[ROBJECT_EMBED_LEN_MAX];
    } as;
 };
RClass
         struct RClass {
             struct RBasic basic;
             rb_classext_t *ptr;
             struct st_table *m_tbl;
             struct st_table *iv_index_tbl;
         };


         struct rb_classext_struct {
             VALUE super;
             struct st_table *iv_tbl;
             struct st_table *const_tbl;
             VALUE origin;
             VALUE refined_class;
             rb_alloc_func_t allocator;
         };
T_DATA
     struct RTypedData {
         struct RBasic basic;
         const rb_data_type_t *type;
         VALUE typed_flag; /* 1 or not */
         void *data;
     };

     struct rb_data_type_struct {
         const char *wrap_struct_name;
         struct {
     ! void (*dmark)(void*);
     ! void (*dfree)(void*);
     ! size_t (*dsize)(const void *);
     ! void *reserved[2];
         } function;
         const rb_data_type_t *parent;
         void *data;
     };
typedef struct RVALUE {
                                   union {
                             !    struct {
                             !        VALUE flags;
                             !        struct RVALUE *next;

RVALUE                       !
                             !
                             !
                                  } free;
                                  struct RBasic basic;
                                  struct RObject object;
                             !    struct RClass klass;
слоты в куче - это RVALUE    !
                             !
                                  struct RFloat flonum;
                                  struct RString string;
                             !    struct RArray array;
union от всех возможных      !
                             !
                                  struct RRegexp regexp;
                                  struct RHash   hash;
системных типов              !    struct RData   data;
                             !    struct RTypedData typeddata;
                             !    struct RStruct rstruct;
тип определяется по флагам   !    struct RBignum bignum;
                             !    struct RFile   file;
размер обычно 40 байт        !
                             !
                                  struct RNode   node;
                                  struct RMatch match;
                             !    struct RRational rational;
                             !    struct RComplex complex;
                                   } as;
                             }   RVALUE;
Корневые объекты
       Главный тред и RubyVM
       Машинный контекст: стек и регистры
       Глобальные константы и переменные
         в том числе из нативных гемов

       Таблица классов
       Generic ivars
       Finalizers и at_exit
Корневой объект          Корневой объект


     Obj1         Obj2        Obj4


     Obj3                     Obj5


                  Obj7        Obj6
Корневой объект          Корневой объект


     Obj1         Obj2        Obj4


     Obj3                     Obj5


                  Obj7        Obj6
Подсчет ссылок
   Корневой объект   1            Корневой объект   2


        Obj1 2           Obj2 1        Obj4 1


        Obj3 1                         Obj5     1


                     1   Obj7          Obj6     1
Mark & Sweep: mark
    Корневой объект          Корневой объект


         Obj1         Obj2        Obj4


         Obj3                     Obj5


                      Obj7        Obj6
Mark & Sweep: mark
    Корневой объект          Корневой объект


         Obj1         Obj2        Obj4


         Obj3                     Obj5


                      Obj7        Obj6
Mark & Sweep: sweep
    Корневой объект          Корневой объект


         Obj1         Obj2        Obj4


         Obj3
Виды ссылок

        Из корневых объектов
        Переменные класса
        Переменные экземпляра
        Содержимое контейнеров
        Локальные переменные
Задачка
    class A
      def a &b; end;
      def initialize
        a(&:to_s)
      end
    end

    def closure_method
      A.new
    end

    closure_method
    GC.start
    puts ObjectSpace.each_object(A){}

                            что будет выведено на экран?
WTF?
Код - тоже объекты
       T_CLASS, T_ICLASS
       T_MODULE
       T_DATA
        iseq = instruction sequence
          method
          block
        proc = iseq + VM/env
Виды ссылок
        Глобальные переменные
        Переменные класса
        Переменные экземпляра
        Содержимое контейнеров
        Локальные переменные
        Замыкания
Как найти?
heap_dump

https://github.com/Vasfed/heap_dump
 Дампит почти полное дерево ссылок
 Без патчей в руби
 Нет оверхеда в простое
 Удобная считалка объектов
Задачка
   class A
     def a &b; end;
     def initialize
       a(&:to_s)
     end
   end

   def closure_method
     A.new
   end

   closure_method
   require 'heap_dump'
   HeapDump.dump
Расследование
$ grep '"name":"A"' dump.json
,{"id":70129858139840,"bt":"T_CLASS","class":70129858139820,"name":"A","methods":
{"a":70129857754000,"initialize":70129858140300}}

$ grep 70129858139840 dump.json | grep T_OBJECT
,{"id":70129858139780,"bt":"T_OBJECT","class":70129858139840}

$ grep 70129858139780 dump.json | grep -v 'id":70129858139780'
,{"id":70129858139720,"bt":"T_DATA","class":70129857815360,"type_name":"VM/
env","size":104,"env":[70129858139780,70129858139720],"local_size":2,...

$ grep 70129858139720 dump.json | grep -v 'id":70129858139720'
,{"id":70129858139700,"bt":"T_DATA","type_name":"proc","envval":
70129858139720,"block":{"iseq":70129858139740,"self":"to_s"}

$ grep 70129858139700 dump.json | grep -v 'id":70129858139700'
,{"id":70129858139760,"bt":"T_ARRAY","class":null,"val":
[null,null,null,null,...,null,null,"to_s",70129858139700,null,null,...]}
Расследование
   class A      object A


                VM/env
                                    Какой-то массив
                                   с 134 символами и
                 proc                     proc




                           Глобальные переменные
Расследование
  //string.c
  static VALUE sym_to_proc(VALUE sym)
  {
      static VALUE sym_proc_cache = Qfalse;
      enum {SYM_PROC_CACHE_SIZE = 67};
      ...

       if (!sym_proc_cache) {
  !   sym_proc_cache = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2);
  !   rb_gc_register_mark_object(sym_proc_cache);
  !    ...
       index = (id % SYM_PROC_CACHE_SIZE) << 1;

      aryp = RARRAY_PTR(sym_proc_cache);
      if (aryp[index] == sym) return aryp[index + 1];
      else {
  !     proc = rb_proc_new(sym_call, (VALUE)id);
  !     aryp[index] = sym; aryp[index + 1] = proc;
  !     return proc;
      }
  }
Правильный ответ

MRI кеширует результаты Symbol#to_proc
В замыкание proc может попасть сам объект
Объект и все, на что он ссылается - останется в памяти до
вытеснения из кеша
Это баг в ruby
На экран будет выведена единица

            https://gist.github.com/4273437
Поиск утечек

        Научиться воспроизводить
        Понять что именно течет
        Снять дамп
        Понять почему течет
        Дальше по желанию
Пример с рельсами

 class LeakController < ApplicationController

   def leak
     ($leak ||= []).push proc{ "some never-callback" }
     render text: "ololo"
   end

 end
Понять что именно течет
 class LeakController < ApplicationController

   def leak
     ($leak ||= []).push proc{ "some never-callback" }
     render text: "ololo"
   end

   def count
     GC.start
     render :json => HeapDump.count_objects([:ApplicationController] +
 ApplicationController.subclasses.map{|c| c.name.to_sym})
   end

   def dump
     fork { HeapDump.dump; exit }
     render :text => "May be Dumped"
   end
 end
Счетчик объектов
       $ curl http://localhost:3000/count
       {
           "total_slots": 183152,
           "free_slots": 53535,
           "basic_types": {
               "T_OBJECT": 5064,
               "T_CLASS": 2723,
               "T_MODULE": 423,
               "T_FLOAT": 82,
               "T_STRING": 74909,
               "T_REGEXP": 1235,
               "T_ARRAY": 21019,
               "T_HASH": 585,
               "T_STRUCT": 199,
               "T_BIGNUM": 2,
               "T_FILE": 8,
               "T_DATA": 12741,
               "T_MATCH": 4,
               "T_COMPLEX": 1,
               "T_RATIONAL": 69,
               "T_NODE": 10038,
               "T_ICLASS": 515
           },
           "user_types": {
               "LeakController": 7
           }
       }
Пример с рельсами
$ grep 'name":"LeakController"' dump.json | grep T_CLASS

,{"id":70281018730340,"bt":"T_CLASS","class":70281018730260,"name":"LeakController",

"methods":{"_layout":70281018415040,
"leak":70281029395580,
"count":70281029394940,
"dump":70281029394280, ...},

"ivs":{"__classpath__":"LeakController",
"@controller_name":"leak",
"@visible_actions":70281018559740,
"@controller_path":"leak",
"@_layout":null,"@action_methods":70281004447860,
"@parent_name":null,"@parent_prefixes":70281009406480,
"@_config":70281007802340,
"@view_context_class":70281004125560},

"super":70281029346060}
Пример с рельсами
$ grep 70211925295180 dump.json
 ,{"id":70211925285360,"bt":"T_DATA",
"class":70211923570840,"type_name":"proc",
"size":72,"is_lambda":0,"blockprocval":null,"envval":
70211925285380,"block":{"iseq":{"id":70211951312640,"name":"block in
leak","filename":"/Users/vasfed/work/railsclub/examples/leaky_app/app/
controllers/leak_controller.rb","line":5,
"type":"block","refs_array_id":70211931351000,
"coverage":null,"klass":null,"cref_stack":
70211931351600,"defined_method_id":0},
"self":70211925295180,
"lfp":70211950222292,"dfp":70211950222292}}
Пример с рельсами
  LeakController    LeakController          LeakController
                                      ...
      proc              proc                    proc


                       Массив


                   Глобальная $leak


                      global_tbl
Profit!
Популярные утечки

Глобальные переменные и их аналоги
@@aa = self
EventMachine.next_tick {...}
Замыкания
Symbol#to_proc aka &:symbol
Стеки зависших тредов/файберов
Что это было?


Общие сведения об устройстве ObjectSpace
Как работает GC и какие объекты выживают
Как искать утечки
???


https://github.com/Vasfed/heap_dump

https://gist.github.com/4273437
@vasfed

Contenu connexe

Tendances

Nucleic Acid Databases (NDB ) of bioinformatics pptx
Nucleic Acid Databases (NDB ) of bioinformatics pptxNucleic Acid Databases (NDB ) of bioinformatics pptx
Nucleic Acid Databases (NDB ) of bioinformatics pptxkarmandeepkaur7
 
Dna isolation from various sources
Dna isolation  from various sourcesDna isolation  from various sources
Dna isolation from various sourcesTamiru Tadele
 
Regression and Classification with R
Regression and Classification with RRegression and Classification with R
Regression and Classification with RYanchang Zhao
 
Haemocytometer ppt animal cell culture
Haemocytometer ppt animal cell cultureHaemocytometer ppt animal cell culture
Haemocytometer ppt animal cell culturevinitha unnikrishnan
 
BT631-12-X-ray_crystallography_protein_crystallization
BT631-12-X-ray_crystallography_protein_crystallizationBT631-12-X-ray_crystallography_protein_crystallization
BT631-12-X-ray_crystallography_protein_crystallizationRajesh G
 
SQL - מודל ישויות קשרים
SQL - מודל ישויות קשריםSQL - מודל ישויות קשרים
SQL - מודל ישויות קשריםמורן אלקובי
 
Nucleic acid database
Nucleic acid databaseNucleic acid database
Nucleic acid databaseEsakkiammal S
 
Differential Interference Contrast Microscope.pptx
Differential Interference Contrast Microscope.pptxDifferential Interference Contrast Microscope.pptx
Differential Interference Contrast Microscope.pptxAdityaSharma772610
 
DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法
DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法
DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法decode2016
 
Dna extraction
Dna extractionDna extraction
Dna extractionGeet_singh
 
subtractive hybridization
subtractive hybridizationsubtractive hybridization
subtractive hybridizationSakshi Saxena
 

Tendances (20)

Nucleic Acid Databases (NDB ) of bioinformatics pptx
Nucleic Acid Databases (NDB ) of bioinformatics pptxNucleic Acid Databases (NDB ) of bioinformatics pptx
Nucleic Acid Databases (NDB ) of bioinformatics pptx
 
Dna isolation from various sources
Dna isolation  from various sourcesDna isolation  from various sources
Dna isolation from various sources
 
Dna2014
Dna2014Dna2014
Dna2014
 
Sql project ..
Sql project ..Sql project ..
Sql project ..
 
Biological databases
Biological databasesBiological databases
Biological databases
 
protein data bank
protein data bankprotein data bank
protein data bank
 
Regression and Classification with R
Regression and Classification with RRegression and Classification with R
Regression and Classification with R
 
Haemocytometer ppt animal cell culture
Haemocytometer ppt animal cell cultureHaemocytometer ppt animal cell culture
Haemocytometer ppt animal cell culture
 
BT631-12-X-ray_crystallography_protein_crystallization
BT631-12-X-ray_crystallography_protein_crystallizationBT631-12-X-ray_crystallography_protein_crystallization
BT631-12-X-ray_crystallography_protein_crystallization
 
Sql Functions And Procedures
Sql Functions And ProceduresSql Functions And Procedures
Sql Functions And Procedures
 
SQL - מודל ישויות קשרים
SQL - מודל ישויות קשריםSQL - מודל ישויות קשרים
SQL - מודל ישויות קשרים
 
Nucleic acid database
Nucleic acid databaseNucleic acid database
Nucleic acid database
 
Differential Interference Contrast Microscope.pptx
Differential Interference Contrast Microscope.pptxDifferential Interference Contrast Microscope.pptx
Differential Interference Contrast Microscope.pptx
 
DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法
DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法
DBP-006_SQL Server 2016 Analysis Services のアーキテクチャとその活用方法
 
Swiss pdb viewer
Swiss pdb viewerSwiss pdb viewer
Swiss pdb viewer
 
Protein modeling
Protein modelingProtein modeling
Protein modeling
 
Introduction to animal cell culture
Introduction to animal cell cultureIntroduction to animal cell culture
Introduction to animal cell culture
 
Dna extraction
Dna extractionDna extraction
Dna extraction
 
subtractive hybridization
subtractive hybridizationsubtractive hybridization
subtractive hybridization
 
Cell lines
Cell linesCell lines
Cell lines
 

En vedette

Ruby Data Types and Data Structures
Ruby Data Types and Data StructuresRuby Data Types and Data Structures
Ruby Data Types and Data StructuresNola Stowe
 
Правильная работа с часовыми поясами в Rails приложении — DevConf 2015
Правильная работа с часовыми поясами в Rails приложении — DevConf 2015Правильная работа с часовыми поясами в Rails приложении — DevConf 2015
Правильная работа с часовыми поясами в Rails приложении — DevConf 2015Андрей Новиков
 
Coub - как мы строили аналитическую платформу на несколько миллиардов событий...
Coub - как мы строили аналитическую платформу на несколько миллиардов событий...Coub - как мы строили аналитическую платформу на несколько миллиардов событий...
Coub - как мы строили аналитическую платформу на несколько миллиардов событий...Ontico
 
Ruby data types and objects
Ruby   data types and objectsRuby   data types and objects
Ruby data types and objectsHarkamal Singh
 
1000 запросов в секунду на rails (Макс Лапшин)
1000 запросов в секунду на rails (Макс Лапшин)1000 запросов в секунду на rails (Макс Лапшин)
1000 запросов в секунду на rails (Макс Лапшин)Ontico
 
Ruby Basics
Ruby BasicsRuby Basics
Ruby BasicsSHC
 

En vedette (8)

Ruby Data Types and Data Structures
Ruby Data Types and Data StructuresRuby Data Types and Data Structures
Ruby Data Types and Data Structures
 
Правильная работа с часовыми поясами в Rails приложении — DevConf 2015
Правильная работа с часовыми поясами в Rails приложении — DevConf 2015Правильная работа с часовыми поясами в Rails приложении — DevConf 2015
Правильная работа с часовыми поясами в Rails приложении — DevConf 2015
 
Coub - как мы строили аналитическую платформу на несколько миллиардов событий...
Coub - как мы строили аналитическую платформу на несколько миллиардов событий...Coub - как мы строили аналитическую платформу на несколько миллиардов событий...
Coub - как мы строили аналитическую платформу на несколько миллиардов событий...
 
Ruby data types and objects
Ruby   data types and objectsRuby   data types and objects
Ruby data types and objects
 
Why Ruby
Why RubyWhy Ruby
Why Ruby
 
1000 запросов в секунду на rails (Макс Лапшин)
1000 запросов в секунду на rails (Макс Лапшин)1000 запросов в секунду на rails (Макс Лапшин)
1000 запросов в секунду на rails (Макс Лапшин)
 
Ruby Basics
Ruby BasicsRuby Basics
Ruby Basics
 
Ruby
RubyRuby
Ruby
 

Память руби изнутри

  • 1. Память Ruby изнутри Василий Федосеев
  • 2. Долгоживущие процессы Проблема утечек памяти и стейтов Нельзя просто перезапустить процесс, как это делает passenger
  • 4. Объекты в MRI Всё - это объекты Их очень много Живут в куче (heap) в фиксированных слотах sizeof(RVALUE) = 40 (обычно)
  • 5. ObjectSpace _id2ref count_objects each_object garbage_collect define_finalizer / undefine_finalizer
  • 7. ObjectSpace RVALUE ObjectSpace Heap RVALUE ... freelist RVALUE Heap Heap free RVALUE free free RVALUE
  • 8. Параметры GC RUBY_GC_MALLOC_LIMIT По умолчанию 8 Мб RUBY_HEAP_MIN_SLOTS 10k 1.9.2 стартует с 17k объектов, 2.0.0 с 15k RUBY_FREE_MIN 4096
  • 9. Однако 2.0.0dev :001 > ObjectSpace.each_object(Hash){} => 118 2.0.0dev :002 > ObjectSpace.each_object(Fixnum){} => 0 2.0.0dev :003 > ObjectSpace.each_object(Symbol){} => 0
  • 10. Не всё - объекты
  • 11. Object ID 2.0.0dev :001 > 0.object_id => 1 2.0.0dev :002 > 1.object_id => 3 2.0.0dev :003 > :a.object_id => 468808 2.0.0dev :004 > "a".object_id => 70199055954380 2.0.0dev :005 > true.object_id => 20 2.0.0dev :006 > false.object_id => 0 2.0.0dev :007 > nil.object_id => 8
  • 12. Object ID Fixnum flag RUBY_Qfalse = 0x00, 101010101011 1 RUBY_Qtrue = 0x14, RUBY_Qnil = 0x08, RUBY_Qundef = 0x34, Symbol id 1100 RUBY_IMMEDIATE_MASK RUBY_FIXNUM_FLAG = = 0x07, 0x01, RUBY_FLONUM_MASK = 0x03, RUBY_FLONUM_FLAG = 0x02, RUBY_SYMBOL_FLAG = 0x0c, RVALUE ptr 000 RUBY_SPECIAL_SHIFT = 8
  • 13. enum ruby_value_type { RBasic RUBY_T_NONE = 0x00, RUBY_T_OBJECT = 0x01, RUBY_T_CLASS = 0x02, RUBY_T_MODULE = 0x03, RUBY_T_FLOAT = 0x04, RUBY_T_STRING = 0x05, RUBY_T_REGEXP = 0x06, RUBY_T_ARRAY = 0x07, RUBY_T_HASH = 0x08, RUBY_T_STRUCT = 0x09, RUBY_T_BIGNUM = 0x0a, RUBY_T_FILE = 0x0b, struct RBasic { RUBY_T_DATA = 0x0c, VALUE flags; RUBY_T_MATCH = 0x0d, RUBY_T_COMPLEX = 0x0e, VALUE klass; RUBY_T_RATIONAL = 0x0f, }; RUBY_T_NIL = 0x11, RUBY_T_TRUE = 0x12, RUBY_T_FALSE = 0x13, RUBY_T_SYMBOL = 0x14, RUBY_T_FIXNUM = 0x15, RUBY_T_UNDEF = 0x1b, RUBY_T_NODE = 0x1c, RUBY_T_ICLASS = 0x1d, RUBY_T_ZOMBIE = 0x1e, RUBY_T_MASK = 0x1f };
  • 14. RObject #define ROBJECT_EMBED_LEN_MAX 3 struct RObject { struct RBasic { VALUE flags; VALUE klass; }; union { ! struct { ! long numiv; ! VALUE *ivptr; struct st_table *iv_index_tbl; ivars[numiv] ! } heap; ! VALUE ary[ROBJECT_EMBED_LEN_MAX]; } as; };
  • 15. RClass struct RClass { struct RBasic basic; rb_classext_t *ptr; struct st_table *m_tbl; struct st_table *iv_index_tbl; }; struct rb_classext_struct { VALUE super; struct st_table *iv_tbl; struct st_table *const_tbl; VALUE origin; VALUE refined_class; rb_alloc_func_t allocator; };
  • 16. T_DATA struct RTypedData { struct RBasic basic; const rb_data_type_t *type; VALUE typed_flag; /* 1 or not */ void *data; }; struct rb_data_type_struct { const char *wrap_struct_name; struct { ! void (*dmark)(void*); ! void (*dfree)(void*); ! size_t (*dsize)(const void *); ! void *reserved[2]; } function; const rb_data_type_t *parent; void *data; };
  • 17. typedef struct RVALUE { union { ! struct { ! VALUE flags; ! struct RVALUE *next; RVALUE ! ! ! } free; struct RBasic basic; struct RObject object; ! struct RClass klass; слоты в куче - это RVALUE ! ! struct RFloat flonum; struct RString string; ! struct RArray array; union от всех возможных ! ! struct RRegexp regexp; struct RHash hash; системных типов ! struct RData data; ! struct RTypedData typeddata; ! struct RStruct rstruct; тип определяется по флагам ! struct RBignum bignum; ! struct RFile file; размер обычно 40 байт ! ! struct RNode node; struct RMatch match; ! struct RRational rational; ! struct RComplex complex; } as; } RVALUE;
  • 18. Корневые объекты Главный тред и RubyVM Машинный контекст: стек и регистры Глобальные константы и переменные в том числе из нативных гемов Таблица классов Generic ivars Finalizers и at_exit
  • 19. Корневой объект Корневой объект Obj1 Obj2 Obj4 Obj3 Obj5 Obj7 Obj6
  • 20. Корневой объект Корневой объект Obj1 Obj2 Obj4 Obj3 Obj5 Obj7 Obj6
  • 21. Подсчет ссылок Корневой объект 1 Корневой объект 2 Obj1 2 Obj2 1 Obj4 1 Obj3 1 Obj5 1 1 Obj7 Obj6 1
  • 22. Mark & Sweep: mark Корневой объект Корневой объект Obj1 Obj2 Obj4 Obj3 Obj5 Obj7 Obj6
  • 23. Mark & Sweep: mark Корневой объект Корневой объект Obj1 Obj2 Obj4 Obj3 Obj5 Obj7 Obj6
  • 24. Mark & Sweep: sweep Корневой объект Корневой объект Obj1 Obj2 Obj4 Obj3
  • 25. Виды ссылок Из корневых объектов Переменные класса Переменные экземпляра Содержимое контейнеров Локальные переменные
  • 26. Задачка class A def a &b; end; def initialize a(&:to_s) end end def closure_method A.new end closure_method GC.start puts ObjectSpace.each_object(A){} что будет выведено на экран?
  • 27. WTF?
  • 28. Код - тоже объекты T_CLASS, T_ICLASS T_MODULE T_DATA iseq = instruction sequence method block proc = iseq + VM/env
  • 29. Виды ссылок Глобальные переменные Переменные класса Переменные экземпляра Содержимое контейнеров Локальные переменные Замыкания
  • 31. heap_dump https://github.com/Vasfed/heap_dump Дампит почти полное дерево ссылок Без патчей в руби Нет оверхеда в простое Удобная считалка объектов
  • 32. Задачка class A def a &b; end; def initialize a(&:to_s) end end def closure_method A.new end closure_method require 'heap_dump' HeapDump.dump
  • 33. Расследование $ grep '"name":"A"' dump.json ,{"id":70129858139840,"bt":"T_CLASS","class":70129858139820,"name":"A","methods": {"a":70129857754000,"initialize":70129858140300}} $ grep 70129858139840 dump.json | grep T_OBJECT ,{"id":70129858139780,"bt":"T_OBJECT","class":70129858139840} $ grep 70129858139780 dump.json | grep -v 'id":70129858139780' ,{"id":70129858139720,"bt":"T_DATA","class":70129857815360,"type_name":"VM/ env","size":104,"env":[70129858139780,70129858139720],"local_size":2,... $ grep 70129858139720 dump.json | grep -v 'id":70129858139720' ,{"id":70129858139700,"bt":"T_DATA","type_name":"proc","envval": 70129858139720,"block":{"iseq":70129858139740,"self":"to_s"} $ grep 70129858139700 dump.json | grep -v 'id":70129858139700' ,{"id":70129858139760,"bt":"T_ARRAY","class":null,"val": [null,null,null,null,...,null,null,"to_s",70129858139700,null,null,...]}
  • 34. Расследование class A object A VM/env Какой-то массив с 134 символами и proc proc Глобальные переменные
  • 35. Расследование //string.c static VALUE sym_to_proc(VALUE sym) { static VALUE sym_proc_cache = Qfalse; enum {SYM_PROC_CACHE_SIZE = 67}; ... if (!sym_proc_cache) { ! sym_proc_cache = rb_ary_tmp_new(SYM_PROC_CACHE_SIZE * 2); ! rb_gc_register_mark_object(sym_proc_cache); ! ... index = (id % SYM_PROC_CACHE_SIZE) << 1; aryp = RARRAY_PTR(sym_proc_cache); if (aryp[index] == sym) return aryp[index + 1]; else { ! proc = rb_proc_new(sym_call, (VALUE)id); ! aryp[index] = sym; aryp[index + 1] = proc; ! return proc; } }
  • 36. Правильный ответ MRI кеширует результаты Symbol#to_proc В замыкание proc может попасть сам объект Объект и все, на что он ссылается - останется в памяти до вытеснения из кеша Это баг в ruby На экран будет выведена единица https://gist.github.com/4273437
  • 37. Поиск утечек Научиться воспроизводить Понять что именно течет Снять дамп Понять почему течет Дальше по желанию
  • 38. Пример с рельсами class LeakController < ApplicationController def leak ($leak ||= []).push proc{ "some never-callback" } render text: "ololo" end end
  • 39. Понять что именно течет class LeakController < ApplicationController def leak ($leak ||= []).push proc{ "some never-callback" } render text: "ololo" end def count GC.start render :json => HeapDump.count_objects([:ApplicationController] + ApplicationController.subclasses.map{|c| c.name.to_sym}) end def dump fork { HeapDump.dump; exit } render :text => "May be Dumped" end end
  • 40. Счетчик объектов $ curl http://localhost:3000/count { "total_slots": 183152, "free_slots": 53535, "basic_types": { "T_OBJECT": 5064, "T_CLASS": 2723, "T_MODULE": 423, "T_FLOAT": 82, "T_STRING": 74909, "T_REGEXP": 1235, "T_ARRAY": 21019, "T_HASH": 585, "T_STRUCT": 199, "T_BIGNUM": 2, "T_FILE": 8, "T_DATA": 12741, "T_MATCH": 4, "T_COMPLEX": 1, "T_RATIONAL": 69, "T_NODE": 10038, "T_ICLASS": 515 }, "user_types": { "LeakController": 7 } }
  • 41. Пример с рельсами $ grep 'name":"LeakController"' dump.json | grep T_CLASS ,{"id":70281018730340,"bt":"T_CLASS","class":70281018730260,"name":"LeakController", "methods":{"_layout":70281018415040, "leak":70281029395580, "count":70281029394940, "dump":70281029394280, ...}, "ivs":{"__classpath__":"LeakController", "@controller_name":"leak", "@visible_actions":70281018559740, "@controller_path":"leak", "@_layout":null,"@action_methods":70281004447860, "@parent_name":null,"@parent_prefixes":70281009406480, "@_config":70281007802340, "@view_context_class":70281004125560}, "super":70281029346060}
  • 42. Пример с рельсами $ grep 70211925295180 dump.json ,{"id":70211925285360,"bt":"T_DATA", "class":70211923570840,"type_name":"proc", "size":72,"is_lambda":0,"blockprocval":null,"envval": 70211925285380,"block":{"iseq":{"id":70211951312640,"name":"block in leak","filename":"/Users/vasfed/work/railsclub/examples/leaky_app/app/ controllers/leak_controller.rb","line":5, "type":"block","refs_array_id":70211931351000, "coverage":null,"klass":null,"cref_stack": 70211931351600,"defined_method_id":0}, "self":70211925295180, "lfp":70211950222292,"dfp":70211950222292}}
  • 43. Пример с рельсами LeakController LeakController LeakController ... proc proc proc Массив Глобальная $leak global_tbl
  • 45. Популярные утечки Глобальные переменные и их аналоги @@aa = self EventMachine.next_tick {...} Замыкания Symbol#to_proc aka &:symbol Стеки зависших тредов/файберов
  • 46. Что это было? Общие сведения об устройстве ObjectSpace Как работает GC и какие объекты выживают Как искать утечки