анализ кучи Java с помощью OQL: подсчет уникальных строк - PullRequest
14 голосов
/ 23 ноября 2011

Я делаю анализ памяти существующего программного обеспечения Java. Есть ли sql 'group by', эквивалентный в oql, чтобы увидеть количество объектов с одинаковыми значениями, но разными экземплярами.

выберите количество (*) из java.lang.String s группа по s.toString ()

Я хотел бы получить список дублированных строк вместе с количеством дубликатов. Цель этого состоит в том, чтобы увидеть случаи с большими числами, чтобы их можно было оптимизировать с помощью String.intern ().

Пример:

"foo"    100
"bar"    99
"lazy fox"    50

и т.д ...

Ответы [ 6 ]

22 голосов
/ 27 февраля 2012

Нижеследующее основано на ответе Питера Долберга и может быть использовано в консоли OQL VisualVM :

var counts={};
var alreadyReturned={};

filter(
  sort(
    map(heap.objects("java.lang.String"),
    function(heapString){
      if( ! counts[heapString.toString()]){
        counts[heapString.toString()] = 1;
      } else {
        counts[heapString.toString()] = counts[heapString.toString()] + 1;
      }
      return { string:heapString.toString(), count:counts[heapString.toString()]};
    }), 
    'lhs.count < rhs.count'),
  function(countObject) {
    if( ! alreadyReturned[countObject.string]){
      alreadyReturned[countObject.string] = true;
      return true;
    } else {
      return false;
    }
   }
  );

Все начинается с вызова map() по всей строкеэкземпляры и для каждой строки, создавая или обновляя объект в массиве counts.Каждый объект имеет поля string и count.

Полученный массив будет содержать одну запись для каждого экземпляра String, каждый из которых будет иметь значение count на единицу больше, чем предыдущая запись для той же строки.Затем результат сортируется в поле count, и результат выглядит примерно так:

{
count = 1028.0,
string = *null*
}

{
count = 1027.0,
string = *null*
}

{
count = 1026.0,
string = *null*
}

...

(в моем тесте строка "*null*" была наиболее распространенной).

последний шаг - отфильтровать это с помощью функции, которая возвращает true для первого вхождения каждой строки.Он использует массив alreadyReturned, чтобы отслеживать, какие строки уже были включены.

9 голосов
/ 24 февраля 2012

Я бы использовал Eclipse Memory Analyzer вместо.

2 голосов
/ 02 февраля 2012

К сожалению, в OQL нет эквивалента "group by".Я предполагаю, что вы говорите об OQL, который используется в jhat и VisualVM.

Хотя есть альтернатива.Если вы используете чистый синтаксис JavaScript вместо синтаксиса «select x from y», то у вас есть все возможности JavaScript для работы.

Несмотря на это, альтернативный способ получения искомой информации нене просто.Например, вот «запрос» OQL, который будет выполнять ту же задачу, что и ваш запрос:

var set={};
sum(map(heap.objects("java.lang.String"),function(heapString){
  if(set[heapString.toString()]){
    return 0;
  }
  else{
    set[heapString.toString()]=true;
    return 1;
  }
}));

В этом примере обычный объект JavaScript имитирует набор (коллекция без дубликатов).Поскольку функция map проходит через каждую строку, набор используется для определения, была ли строка уже видна.Дубликаты не учитываются в сумме (возвращаемое значение 0), но новые строки (возвращаемое значение 1).

1 голос
/ 20 июня 2017

Гораздо более эффективный запрос:

var countByValue = {};

// Scroll the strings
heap.forEachObject(
  function(strObject) {
    var key = strObject.toString();
    var count = countByValue[key];
    countByValue[key] = count ? count + 1 : 1;
  },
  "java.lang.String",
  false
);

// Transform the map into array
var mapEntries = [];
for (var i = 0, keys = Object.keys(countByValue), total = keys.length; i < total; i++) {
  mapEntries.push({
    count : countByValue[keys[i]],
    string : keys[i]
  });
}

// Sort the counts
sort(mapEntries, 'rhs.count - lhs.count');
0 голосов
/ 15 июля 2019

Метод 1

Вы можете выбрать все строки и затем использовать терминал для их агрегирования.

  1. Увеличить ограничение oql в файлах конфигурации visual vm
  2. перезапустите visual vm
  3. oql, чтобы получить все строки
  4. скопируйте и вставьте их в vim
  5. очистите данные с помощью макросов vim, чтобы было
  6. sort | uniq -c чтобы получить счет.

Метод 2

  1. Используйте инструмент для сброса всех полей объекта интересующего вас класса (https://github.com/josephmate/DumpHprofFields можетсделайте это)
  2. Используйте bash для выбора интересующих вас строк
  3. Используйте bash для агрегирования
0 голосов
/ 20 июня 2017

Просто опубликуйте мое решение и опыт при выполнении аналогичной проблемы для других ссылок.

var counts = {};
var alreadyReturned = {};
top(
filter(
    sort(
        map(heap.objects("java.lang.ref.Finalizer"),
            function (fobject) {
                var className = classof(fobject.referent)
                if (!counts[className]) {
                    counts[className] = 1;
                } else {
                    counts[className] = counts[className] + 1;
                }
                return {string: className, count: counts[className]};
            }),
        'rhs.count-lhs.count'),
    function (countObject) {
        if (!alreadyReturned[countObject.string]) {
            alreadyReturned[countObject.string] = true;
            return true;
        } else {
            return false;
        }
    }),
    "rhs.count > lhs.count", 10);

Предыдущий код выведет 10 лучших классов, используемых java.lang.ref.Finalizer.
Советы:
1. Функция сортировки с использованием функции XXX НЕ работает в моей Mac OS.
2. Функция classof может возвращать класс референта.(Я пытался использовать fobject.referent.toString () -> это вернуло много org.netbeans.lib.profiler.heap.InstanceDump. Это также потеряло много моего времени).

...