SlideShare uma empresa Scribd logo
1 de 47
Baixar para ler offline
Память 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

Mais conteúdo relacionado

Mais procurados

TypeScript: Basic Features and Compilation Guide
TypeScript: Basic Features and Compilation GuideTypeScript: Basic Features and Compilation Guide
TypeScript: Basic Features and Compilation GuideNascenia IT
 
The Power of Regular Expression: use in notepad++
The Power of Regular Expression: use in notepad++The Power of Regular Expression: use in notepad++
The Power of Regular Expression: use in notepad++Anjesh Tuladhar
 
Les02 (restricting and sorting data)
Les02 (restricting and sorting data)Les02 (restricting and sorting data)
Les02 (restricting and sorting data)Achmad Solichin
 
Opérateurs Ensemblistes | SQL Oracle
Opérateurs Ensemblistes | SQL OracleOpérateurs Ensemblistes | SQL Oracle
Opérateurs Ensemblistes | SQL Oraclewebreaker
 
実務で役立つデータベースの活用法
実務で役立つデータベースの活用法実務で役立つデータベースの活用法
実務で役立つデータベースの活用法Soudai Sone
 
Apache Sling : JCR, OSGi, Scripting and REST
Apache Sling : JCR, OSGi, Scripting and RESTApache Sling : JCR, OSGi, Scripting and REST
Apache Sling : JCR, OSGi, Scripting and RESTCarsten Ziegeler
 
LinkedList vs Arraylist- an in depth look at java.util.LinkedList
LinkedList vs Arraylist- an in depth look at java.util.LinkedListLinkedList vs Arraylist- an in depth look at java.util.LinkedList
LinkedList vs Arraylist- an in depth look at java.util.LinkedListMarcus Biel
 
The Leader and the 008 Field in MARC Bibliographic Records
The Leader and the 008 Field in MARC Bibliographic RecordsThe Leader and the 008 Field in MARC Bibliographic Records
The Leader and the 008 Field in MARC Bibliographic RecordsLowell Ashley
 
Regular expression
Regular expressionRegular expression
Regular expressionLarry Nung
 
Cours java avance débutant facile l'essentiel swing ,events
Cours java avance débutant facile l'essentiel swing ,events Cours java avance débutant facile l'essentiel swing ,events
Cours java avance débutant facile l'essentiel swing ,events Houssem Hamrouni
 
「正義の味方」と「悪の組織」の違いと人材の育成や組織論
「正義の味方」と「悪の組織」の違いと人材の育成や組織論「正義の味方」と「悪の組織」の違いと人材の育成や組織論
「正義の味方」と「悪の組織」の違いと人材の育成や組織論新潟コンサルタント横田秀珠
 
JEE Course - JEE Overview
JEE Course - JEE  OverviewJEE Course - JEE  Overview
JEE Course - JEE Overviewodedns
 
JSON Array Indexes in MySQL
JSON Array Indexes in MySQLJSON Array Indexes in MySQL
JSON Array Indexes in MySQLNorvald Ryeng
 
09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...
09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...
09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...Hamza SAID
 
Python 3のWebシステムでDDDに入門してみた
Python 3のWebシステムでDDDに入門してみたPython 3のWebシステムでDDDに入門してみた
Python 3のWebシステムでDDDに入門してみたHiromu Yakura
 
絶対落ちないアプリの作り方
絶対落ちないアプリの作り方絶対落ちないアプリの作り方
絶対落ちないアプリの作り方Fumihiko Shiroyama
 

Mais procurados (20)

TypeScript: Basic Features and Compilation Guide
TypeScript: Basic Features and Compilation GuideTypeScript: Basic Features and Compilation Guide
TypeScript: Basic Features and Compilation Guide
 
The Power of Regular Expression: use in notepad++
The Power of Regular Expression: use in notepad++The Power of Regular Expression: use in notepad++
The Power of Regular Expression: use in notepad++
 
Les02 (restricting and sorting data)
Les02 (restricting and sorting data)Les02 (restricting and sorting data)
Les02 (restricting and sorting data)
 
Object Oriented Javascript
Object Oriented JavascriptObject Oriented Javascript
Object Oriented Javascript
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Opérateurs Ensemblistes | SQL Oracle
Opérateurs Ensemblistes | SQL OracleOpérateurs Ensemblistes | SQL Oracle
Opérateurs Ensemblistes | SQL Oracle
 
実務で役立つデータベースの活用法
実務で役立つデータベースの活用法実務で役立つデータベースの活用法
実務で役立つデータベースの活用法
 
Apache Sling : JCR, OSGi, Scripting and REST
Apache Sling : JCR, OSGi, Scripting and RESTApache Sling : JCR, OSGi, Scripting and REST
Apache Sling : JCR, OSGi, Scripting and REST
 
LinkedList vs Arraylist- an in depth look at java.util.LinkedList
LinkedList vs Arraylist- an in depth look at java.util.LinkedListLinkedList vs Arraylist- an in depth look at java.util.LinkedList
LinkedList vs Arraylist- an in depth look at java.util.LinkedList
 
The Leader and the 008 Field in MARC Bibliographic Records
The Leader and the 008 Field in MARC Bibliographic RecordsThe Leader and the 008 Field in MARC Bibliographic Records
The Leader and the 008 Field in MARC Bibliographic Records
 
Collada Reference Card 1.4
Collada Reference Card 1.4Collada Reference Card 1.4
Collada Reference Card 1.4
 
Regular expression
Regular expressionRegular expression
Regular expression
 
Cours java avance débutant facile l'essentiel swing ,events
Cours java avance débutant facile l'essentiel swing ,events Cours java avance débutant facile l'essentiel swing ,events
Cours java avance débutant facile l'essentiel swing ,events
 
「正義の味方」と「悪の組織」の違いと人材の育成や組織論
「正義の味方」と「悪の組織」の違いと人材の育成や組織論「正義の味方」と「悪の組織」の違いと人材の育成や組織論
「正義の味方」と「悪の組織」の違いと人材の育成や組織論
 
Java 11 to 17 : What's new !?
Java 11 to 17 : What's new !?Java 11 to 17 : What's new !?
Java 11 to 17 : What's new !?
 
JEE Course - JEE Overview
JEE Course - JEE  OverviewJEE Course - JEE  Overview
JEE Course - JEE Overview
 
JSON Array Indexes in MySQL
JSON Array Indexes in MySQLJSON Array Indexes in MySQL
JSON Array Indexes in MySQL
 
09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...
09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...
09 visual basic .net - modules de classes-constructeurs, encapsulation, herit...
 
Python 3のWebシステムでDDDに入門してみた
Python 3のWebシステムでDDDに入門してみたPython 3のWebシステムでDDDに入門してみた
Python 3のWebシステムでDDDに入門してみた
 
絶対落ちないアプリの作り方
絶対落ちないアプリの作り方絶対落ちないアプリの作り方
絶対落ちないアプリの作り方
 

Destaque

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
 

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