2017-02-07 1 views
4

У меня есть утечка памяти в приложении Rails 4.2.6. Контроллер выделяет большой объект GaragesPresenter в качестве переменной экземпляра, который должен быть удален и мусор, собранный после завершения запроса. Однако я вижу, что этого никогда не бывает.Ошибка утечки памяти: класс контроллера, содержащий ссылку на экземпляр

def show 
    @garage = GaragesPresenter.new(@garage, view_context) 
    respond_to do |format| 
    format.html 
    end 
end 

Я вижу, что ссылка на экземпляр GaragesPresenter проводится экземпляром GaragesController, а экземпляр к тому, что в настоящее время удерживается GaragesController класса. Это верно уже после того, как запрос завершен и вызывается GC.start. Почему класс GaragesController содержит ссылку на экземпляр?

Я знаю это, потому что я создал дамп кучи с:

require 'objspace' 
... 
GC.start 
file = File.open("/tmp/dumpfile", 'w') 
ObjectSpace.dump_all(output: file) 

И в конечном файле я вижу следующие три объекта:

Следующий объект является GaragesPresenter, который очень большой:

{"address":"0x7fd077217e20", "type":"OBJECT", "class":"0x7fd074a04618", "ivars":7, "references":["0x7fd0772bf940", "0x7fd077711480", "0x7fd077748188", "0x7fd077772898", "0x7fd07720c778", "0x7fd0771ef8d0", "0x7fd0771ef8d0"], "file":"/Users/dyoung/workspace/commutyble/site-app/app/controllers/garages_controller.rb", "line":19, "method":"new", "generation":35, "memsize":56, "flags":{"wb_protected":true, "old":true, "marked":true}}

Ссылки на выше объект проводится экземпляр GaragesController (ожидается, поскольку метод шоу выделяет выступающему в качестве переменного экземпляра):

{"address":"0x7fd0727559f0", "type":"OBJECT", "class":"0x7fd0727865a0", "ivars":22, "references":["0x7fd0727558b0", "0x7fd072755888", "0x7fd072755838", "0x7fd0732400e0", "0x7fd072754a50", "0x7fd0734c5658", "0x7fd07704e878", "0x7fd0732ab020", "0x7fd072785ee8", "0x7fd077217e20", "0x7fd0771ffe10", "0x7fd07720cde0", "0x7fd0732a82d0"], "file":"/Users/dyoung/.rvm/gems/ruby-2.1.0/gems/actionpack-4.2.6/lib/action_controller/metal.rb", "line":237, "method":"new", "generation":35, "memsize":176, "flags":{"wb_protected":true, "old":true, "marked":true}}

Ссылка на экземпляр выше GaragesController в настоящее время проводимый классом GaragesController, предположительно предотвращающий сбор гарабеков. Зачем??

{"address":"0x7fd0727865a0", "type":"CLASS", "class":"0x7fd0726a7260", "name":"GaragesController", "references":["0x7fd0727559f0", "0x7fd0726a72b0"], "file":"/Users/dyoung/.rvm/gems/ruby-2.1.0/gems/activesupport-4.2.6/lib/active_support/callbacks.rb", "line":435, "method":"instance_exec", "generation":35, "memsize":672, "flags":{"wb_protected":true, "old":true, "marked":true}}

+1

Кажется '[WeakRef] (рубин-док. org/stdlib-1.9.3/libdoc/weakref/rdoc/WeakRef.html) 'может помочь – oklas

ответ

3

Вы должны использовать WeakRef

Слабое Reference класс, который позволяет ссылаться объект быть сборщиком мусора. A WeakRef может использоваться точно так же, как объект, который он ссылается .

foo = Object.new 

foo = WeakRef.new(foo) # Creates a weak reference to orig 

ObjectSpace.garbage_collect 

p foo.to_s  # should raise exception (recycled) 

Случай использования там, где используются два ссылки на объект. Сначала мастер, а второй слабый. Ваш объект не будет собираться мусором до тех пор, пока не будет использована первая мастер-ссылка. Используйте главную ссылку (общую переменную) в объекте, который проживает то же самое или больше времени, чем нам нужен ссылочный объект. И внутри ссылочного объекта используется слабая ссылка.

Это соответствующая практика для этого случая. На других языках с сборщиком мусора тоже, например, в perl. У библиотек C++ слишком много решений для стратегий управления памятью. Сборщик мусора не может удалить мусор (объекты), когда он используется. Если объект относится к другому, а другой относится к первому, это означает, что он используется. Так что это не мусор - он полезен для «сборщика мусора». Но на самом деле это мусор - и это утечка памяти.

График ссылок на объекты не должен иметь циклов или циклов. Если нам нужна ссылка, которая формирует цикл или цикл в графе объектов-ссылок, нам нужно использовать по крайней мере одну квазирежущую слабую ссылку в каждом.

+0

Спасибо за подсказку. Но я не хочу, чтобы мой объект '@ garage' был собран мусором, пока я не закончил его использование. Кажется, что если я использую 'WeakRef', тогда есть шанс, что это может быть сбор мусора до того, как метод контроллера завершится. Я ошибаюсь? – davidgyoung

+0

Всегда будет хорошо. Я немного улучшил ответ. Используйте мастер-ссылку в том месте, где не будет собран мусор. – oklas

+0

Да, похоже, что это может работать с улучшением. Но это, похоже, имеет тот же эффект от меня, просто устанавливая '@garage = nil' в конце моего метода. Хотя любой из них может быть разумным обходным путем, я понимаю, что я не должен * нуждаться в этом, чтобы сделать это в Rails. Я бы хотел сначала понять, что я делаю неправильно, если что-нибудь, что вызывает это неожиданное поведение. – davidgyoung

1

GaragesPresenter содержит ссылку на view_context

@garage = GaragesPresenter.new(@garage, view_context) 

view_context возвращает instance of of a view class, который содержит ссылку на self, который является контроллером ссылающееся:

# File actionview/lib/action_view/rendering.rb, line 71 
def view_context 
    view_context_class.new(view_renderer, view_assigns, self) 
end 
+0

Спасибо. Это не должно мешать сборке мусора, верно? Я думаю, что это нормально для ведущего, чтобы содержать ссылку на экземпляр контроллера, потому что оба объекта должны существовать только для срока службы запроса. Оба объекта должны иметь право на сбор мусора после завершения запроса. – davidgyoung

+0

Он не должен, если только что-то не держится ни за контроллер, ни за ведущий, и в этом случае оба сохраняют существующий. Я бы более пристально посмотрел на то, будут ли какие-то константы мутированы. – fylooi

+0

Да, в моем случае у меня есть доказательства того, что что-то держит ссылку на контроллер. Что вы подразумеваете под «независимо от того, становятся ли какие-то константы мутированными»? – davidgyoung

 Смежные вопросы

  • Нет связанных вопросов^_^