Статьи

Объекты Ruby в куче JRuby во время выполнения

На прошлой неделе я написал статью об использовании jhat для получения статического снимка объектов Ruby в куче JRuby . Следующий шаг — попытаться получить похожую информацию из запущенного приложения с периодическими обновлениями. Другими словами, загляните внутрь кода JRuby во время его выполнения на JVM и выясните количество объектов Ruby, которые были распределены с разбивкой по классу Ruby для каждого объекта.

Инструмент, который я выбрал на этот раз, был BTrace . «B» обозначает «Байт-код», а BTrace предоставляет возможность профилирования «накатить свой». Другими словами, вы получаете полный контроль над тем, что инструментируется в вашем Java-приложении. Компромисс, по сравнению с другими инструментами профилирования, заключается в том, что вы должны выполнять больше работы. В частности, вы должны написать сценарии, которые контролируют инструментарий. К счастью, язык сценариев знаком: Java вместе с некоторыми аннотациями, которые предоставляет BTrace.

BTrace — простая утилита командной строки, для которой просто необходим идентификатор процесса JVM для инструмента и имя файла сценария (вы можете предоставить исходный файл .java или использовать btracec для создания файла .class). Чтобы поэкспериментировать с этим, мне нужна была программа на Ruby, которая создавала бы множество объектов. Я написал это простое приложение:

class Every
end

class EveryOther
end

puts "Hello World"
i = 0;
all = Array.new
some = Array.new
while (true) 
  some[i] = EveryOther.new if (i%2==0)
  all[i] = Every.new
  puts i if (i%1000000==0)
  i+=1
end

У него просто есть бесконечный цикл, который распределяет объекты. Я использую JRuby 1.1 для его запуска и передаю -J-Xmx2048m в командной строке JRuby, чтобы программа могла довольно долго запускаться, прежде чем использовать все пространство кучи и выйти из нее с помощью OutOfMemoryError .

Мой скрипт BTrace:

package org.jruby;

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.aggregation.Aggregation;
import com.sun.btrace.aggregation.AggregationFunction;
import com.sun.btrace.aggregation.AggregationKey;

/**
 * Create a histogram of RubyObject instances, 
 * keyed by RubyClass classId.
 * @author Gregg Sporar
*/
@BTrace
public class RubyClassHistogram {
   private static Aggregation sum = newAggregation(AggregationFunction.SUM);

   /*
    * Instrument the public RubyObject contstructor 
    */
    @OnMethod(
        clazz="org.jruby.RubyObject",
        method="<init>",
        type="void (org.jruby.Ruby, org.jruby.RubyClass)"
    ) 
    public static void onNewObject(org.jruby.RubyObject self,
                                   org.jruby.Ruby ruby,
                                   org.jruby.RubyClass rubyClass) {
        String classId = rubyClass.classId;
        AggregationKey key = newAggregationKey(classId);
        addToAggregation(sum, key, 1);
    }

    @OnTimer(4000) 
    public static void print() {
        printAggregation("RubyObject Instances Created:", sum);
    }
}

 

BTrace вызывает мой метод print () каждые 4 секунды для отображения текущих значений в гистограмме. Например:

Создано экземпляров RubyObject:

строка 18

EveryOther 2934364

Every 5868731

 

Пожалуйста, обратите внимание:

 

  • Гистограмма показывает количество созданных объектов , а не количество объектов, которые в данный момент находятся в куче. В примере приложения-игрушки, которое я использовал, эти числа одинаковы, но в реальных приложениях число живых объектов обычно меньше общего числа созданных объектов. Я не очень хорошо разбираюсь во внутренностях JRuby, но я предполагаю, что вышеприведенный скрипт BTrace можно было бы усовершенствовать, чтобы он также использовал метод в JRuby, который удаляет объект Ruby из кучи JRuby.
  • BTrace требует JDK 6 или выше.
  • BTrace все еще находится в стадии разработки — используйте его на свой страх и риск. Из того, что я видел и слышал, он выглядит надежным и стабильным, но я не знаю, буду ли я использовать его в производственной системе. В частности, у меня пока нет достаточного опыта работы с ним, чтобы иметь представление о количестве накладных расходов, которые он будет вносить при использовании со скриптом, подобным показанному здесь.

С http://blogs.sun.com/gregg