Отследить проблему PermGen с JRuby on Rails в Tomcat - PullRequest
5 голосов
/ 16 июля 2010

Мы запускаем небольшое веб-приложение, написанное на JRuby на Rails и работающее под Tomcat. Мы используем серверную часть Spring, которая используется совместно с другим рабочим веб-приложением. К сожалению, мы продолжаем сталкиваться с проблемами PermGen.

ОС: Ubuntu Linux 2.6.24-24-сервер №1 SMP x86_64 GNU / Linux Java: 1.6.0_21 Tomcat: 6.0.28 JRuby: 1.5.0 Рельсы: 2.3.7

В настоящее время мы сканируем Google, Yahoo и Baidu, поэтому использование сайта возросло. Я следил за Tomcat с помощью JConsole, и мы определенно видим проблему с чрезмерным количеством классов. Когда запускается tomcat, у нас загружается около 12 000 классов. Через 8 часов у нас загружено почти 75 000 классов. PermGen идет от 100 МБ до 460 МБ одновременно.

Разгрузка классов работает, но она выгружает только ~ 500 классов за тот же 8-часовой период. Кажется, что PermGen никогда не собирают.

Мы работаем со следующими параметрами VM для Tomcat:

-Xms2048m -Xmx2048m -XX:MaxPermSize=512m -XX:PermSize=128m \
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=4 \
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

Очевидно, что есть какая-то утечка. Вопрос как где? Любой совет, как отследить, кто и что за это отвечает? Я надеюсь, что это действительно глупая ошибка с нашей стороны, но я не уверен, с чего начать.

Любой совет будет принят с благодарностью.

РЕДАКТИРОВАТЬ

Похоже, мы видим один новый класс, созданный для каждого входящего запроса.

РЕДАКТИРОВАТЬ 2

Это определенно связано с JRuby. Используя JConsole, я включил режим Verbose для загрузчика классов. Вот пример из catalina.out:

[Loaded anon_class1275113147_895127379 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
[Loaded anon_class1354333392_895127376 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
[Loaded anon_class1402528430_895127373 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]

Таким образом, возникает вопрос, как мне отследить группу, ответственную за создание этих дополнительных классов?

РЕДАКТИРОВАТЬ 3

Не уверен, что это проблема, но каким-то образом мы получаем безумное количество загрузчиков классов. Побежал jmap -permstat PID и получил:

class_loader  classes bytes       parent_loader   alive?              type
total = 1320  135748  947431296   N/A             alive=1, dead=1319  N/A

Это кажется немного чрезмерным. Большинство из них являются одним из трех типов загрузчиков классов: sun.reflect.DelegatingClassLoader, org.jruby.util.JRubyClassLoader или org.jruby.util.ClassCache$OneShotClassLoader. Опять же, пример вывода из jmap -permstat:

class_loader            classes bytes      parent_loader           alive?  type
0x00007f71f4e93d58      1       3128       0x00007f71f4d54680      dead    sun/reflect/DelegatingClassLoader@0x00007f72ef9a6dc0
0x00007f721e51e2a0      57103   316038936  0x00007f720431c958      dead    org/jruby/util/JRubyClassLoader@0x00007f72f2fd1158
0x00007f72182f2b10      4       12944      0x00007f721d7f3030      dead    org/jruby/util/JRubyClassLoader@0x00007f72f2fd1158
0x00007f721d7d50d8      9       457520     0x00007f720431c958      dead    org/jruby/util/ClassCache$OneShotClassLoader@0x00007f72f3ce2368

Ответы [ 4 ]

6 голосов
/ 16 июля 2010

PermGen - определенно проблема с приложениями на основе JRuby. Я не удивлен, что CMS не собирает много. Как правило, нет настоящей утечки памяти, скорее приложение просто тяжелое и тяжелое для permgen и еще не выровнялось.

Я могу предложить несколько вариантов:

  1. Поднимите permgen еще дальше, чтобы увидеть, можете ли вы найти точку выравнивания.
  2. Посмотрите, сможете ли вы без проблем запустить ваше приложение в чисто интерпретированном режиме (-Djruby.compile.mode = OFF). Это должно избавить от большого количества классов, заполняющих ваш permgen.
  3. Попробуйте запустить с Rails 2.2 и выше режим threadsafe!. Запуск вашего приложения в одной среде выполнения - это еще один способ получить большую экономию памяти, и это относится и к permgen.

РЕДАКТИРОВАТЬ: К вашему сведению, этот вопрос оказался ошибка JRuby . Релизы 1.5.2 и 1.6 должны решить эту конкретную проблему. Мои комментарии выше в целом остаются в силе.

2 голосов
/ 30 июля 2010

Просто, чтобы предоставить простой пример, показывающий эту проблему и обходной путь:

require 'java'
include_class java.util.ArrayList

list = ArrayList.new
list << 3
list << 2
list << 1

3.times do
  new_list = list.sort { |a, b| a <=> b}
  #new_list = list.to_a.sort { |a, b| a <=> b}
  puts new_list
end

Предположим, что имя файла - test_classload.rb, следующие выходные данные: $ jruby -J-XX: + TraceClassLoading test_classload.рб |grep anon_class
[загруженный anon_class819349464_307995535 из JVM_DefineClass ]
[загруженный anon_class729155693_307995574 из JVM_DefineClass ] 1010
1010 *1010* 1010 *1010* 1010 *1010* 1010 *1010* 1010 *1010* * * * * * * * * * *1010* * * * * * * * *1010* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1010* * * *1010* * * * * * * * * *1010* * * * * * * * * *1010* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * не так уж и стоило.

Если переключиться на закомментированную строку, вывод будет пустым: anon_class не загружен.

2 голосов
/ 30 июля 2010

У нас была похожая проблема с веб-приложением Sinatra, использующим JRuby 1.5.1: опция JVM TraceClassLoading выводит anon_class *, загружаемый вместе с каждым запросом.

Потратив некоторое время, чтобы сузить место загрузки этого анонимного класса,что делается путем распечатки операторов трассировки в консоль, мы наконец выяснили, что это было вызвано вызовом отсутствующего метода в объекте Java.

Этот вызов вызвал JRuby для добавления этого недостающего метода в объект Java.Этот процесс создал новый одноэлементный класс JRuby, который был назван «anon_class», за которым следовали некоторые хеш-значения.Поскольку это тип класса, он остается в PermGen и никогда не собирается GC.

Обходной путь - избегать вызова этого отсутствующего метода или предоставлять реализацию.В нашем случае мы пытались вызвать метод сортировки с блоком объекта Java ArrayList.Если мы сначала вызовем метод to_a для преобразования Java ArrayList в массив JRuby, то сортировка с помощью блока не создаст anon_class.

Итак, я бы предложил пересмотреть код мест, обращающихся к объекту Java из JRudy..

1 голос
/ 16 июля 2010

Существуют инструменты профилирования и люди, которые знают, как ими пользоваться.Боюсь, я не один из них.

Совет грубой силы:

Перезагрузите свой Tomcat каждые 8 ​​часов.Общее время простоя, как видят ваши пользователи, будет очень приемлемым.Проблема решена;)


РЕДАКТИРОВАТЬ

О, все в порядке! Буровой раствор .

...