SlideShare a Scribd company logo
Память 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

More Related Content

What's hot

Redux training
Redux trainingRedux training
Redux training
dasersoft
 
Alphorm.com Formation React : Les fondamentaux
Alphorm.com Formation React : Les fondamentauxAlphorm.com Formation React : Les fondamentaux
Alphorm.com Formation React : Les fondamentaux
Alphorm
 
Scala Intro
Scala IntroScala Intro
React - Start learning today
React - Start learning today React - Start learning today
React - Start learning today
Nitin Tyagi
 
cours java complet-2.pdf
cours java complet-2.pdfcours java complet-2.pdf
cours java complet-2.pdf
Jaouad Assabbour
 
Introduction to GraphQL
Introduction to GraphQLIntroduction to GraphQL
Introduction to GraphQL
Brandon Minnick, MBA
 
OSGi, Scripting and REST, Building Webapps With Apache Sling
OSGi, Scripting and REST, Building Webapps With Apache SlingOSGi, Scripting and REST, Building Webapps With Apache Sling
OSGi, Scripting and REST, Building Webapps With Apache Sling
Carsten Ziegeler
 
Js: master prototypes
Js: master prototypesJs: master prototypes
Js: master prototypes
Barak Drechsler
 
Monads do not Compose
Monads do not ComposeMonads do not Compose
Monads do not Compose
Philip Schwarz
 
Workshop 21: React Router
Workshop 21: React RouterWorkshop 21: React Router
Workshop 21: React Router
Visual Engineering
 
RxJS - The Basics & The Future
RxJS - The Basics & The FutureRxJS - The Basics & The Future
RxJS - The Basics & The Future
Tracy Lee
 
Content Storage With Apache Jackrabbit
Content Storage With Apache JackrabbitContent Storage With Apache Jackrabbit
Content Storage With Apache Jackrabbit
Jukka Zitting
 
Présentation Angular 2
Présentation Angular 2 Présentation Angular 2
Présentation Angular 2
Cynapsys It Hotspot
 
Defending against Java Deserialization Vulnerabilities
 Defending against Java Deserialization Vulnerabilities Defending against Java Deserialization Vulnerabilities
Defending against Java Deserialization Vulnerabilities
Luca Carettoni
 
Visitor design pattern
Visitor design patternVisitor design pattern
Visitor design pattern
Salem-Kabbani
 
LKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan Roock
LKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan RoockLKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan Roock
LKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan Roock
Lean Kanban Central Europe
 
JavaScript for ABAP Programmers - 2/7 Data Types
JavaScript for ABAP Programmers - 2/7 Data TypesJavaScript for ABAP Programmers - 2/7 Data Types
JavaScript for ABAP Programmers - 2/7 Data Types
Chris Whealy
 
JavaScript Object Oriented Programming Cheat Sheet
JavaScript Object Oriented Programming Cheat SheetJavaScript Object Oriented Programming Cheat Sheet
JavaScript Object Oriented Programming Cheat Sheet
HDR1001
 
Kultura Hispanoameryki
Kultura HispanoamerykiKultura Hispanoameryki
Kultura Hispanoamerykihiszpanski2lo
 
An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...
An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...
An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...
joaomatosf_
 

What's hot (20)

Redux training
Redux trainingRedux training
Redux training
 
Alphorm.com Formation React : Les fondamentaux
Alphorm.com Formation React : Les fondamentauxAlphorm.com Formation React : Les fondamentaux
Alphorm.com Formation React : Les fondamentaux
 
Scala Intro
Scala IntroScala Intro
Scala Intro
 
React - Start learning today
React - Start learning today React - Start learning today
React - Start learning today
 
cours java complet-2.pdf
cours java complet-2.pdfcours java complet-2.pdf
cours java complet-2.pdf
 
Introduction to GraphQL
Introduction to GraphQLIntroduction to GraphQL
Introduction to GraphQL
 
OSGi, Scripting and REST, Building Webapps With Apache Sling
OSGi, Scripting and REST, Building Webapps With Apache SlingOSGi, Scripting and REST, Building Webapps With Apache Sling
OSGi, Scripting and REST, Building Webapps With Apache Sling
 
Js: master prototypes
Js: master prototypesJs: master prototypes
Js: master prototypes
 
Monads do not Compose
Monads do not ComposeMonads do not Compose
Monads do not Compose
 
Workshop 21: React Router
Workshop 21: React RouterWorkshop 21: React Router
Workshop 21: React Router
 
RxJS - The Basics & The Future
RxJS - The Basics & The FutureRxJS - The Basics & The Future
RxJS - The Basics & The Future
 
Content Storage With Apache Jackrabbit
Content Storage With Apache JackrabbitContent Storage With Apache Jackrabbit
Content Storage With Apache Jackrabbit
 
Présentation Angular 2
Présentation Angular 2 Présentation Angular 2
Présentation Angular 2
 
Defending against Java Deserialization Vulnerabilities
 Defending against Java Deserialization Vulnerabilities Defending against Java Deserialization Vulnerabilities
Defending against Java Deserialization Vulnerabilities
 
Visitor design pattern
Visitor design patternVisitor design pattern
Visitor design pattern
 
LKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan Roock
LKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan RoockLKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan Roock
LKCE16 - Verbesserung und Alignment mit dem Nordstern-Konzept von Stefan Roock
 
JavaScript for ABAP Programmers - 2/7 Data Types
JavaScript for ABAP Programmers - 2/7 Data TypesJavaScript for ABAP Programmers - 2/7 Data Types
JavaScript for ABAP Programmers - 2/7 Data Types
 
JavaScript Object Oriented Programming Cheat Sheet
JavaScript Object Oriented Programming Cheat SheetJavaScript Object Oriented Programming Cheat Sheet
JavaScript Object Oriented Programming Cheat Sheet
 
Kultura Hispanoameryki
Kultura HispanoamerykiKultura Hispanoameryki
Kultura Hispanoameryki
 
An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...
An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...
An Overview of Deserialization Vulnerabilities in the Java Virtual Machine (J...
 

Viewers also liked

Ruby Data Types and Data Structures
Ruby Data Types and Data StructuresRuby Data Types and Data Structures
Ruby Data Types and Data Structures
Nola 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 objects
Harkamal Singh
 
Why Ruby
Why RubyWhy Ruby
Why Ruby
Daniel Lv
 
1000 запросов в секунду на rails (Макс Лапшин)
1000 запросов в секунду на rails (Макс Лапшин)1000 запросов в секунду на rails (Макс Лапшин)
1000 запросов в секунду на rails (Макс Лапшин)Ontico
 
Ruby Basics
Ruby BasicsRuby Basics
Ruby Basics
SHC
 
Ruby
RubyRuby

Viewers also liked (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 и какие объекты выживают Как искать утечки