Подтвердить что ты не робот

Java-анализ кучи с oql: подсчет уникальных строк

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

выберите count (*) из java.lang.String s group by s.toString()

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

Пример:

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

и т.д...

4b9b3361

Ответ 1

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

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() по всем экземплярам String и для каждой строки, создающей или обновляющей объект в массиве counts. Каждый объект имеет поле string и count.

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

{
count = 1028.0,
string = *null*
}

{
count = 1027.0,
string = *null*
}

{
count = 1026.0,
string = *null*
}

...

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

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

Ответ 3

К сожалению, в 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-объект имитирует набор (коллекция без дубликатов). Поскольку функция карты проходит через каждую строку, этот набор используется для определения того, была ли строка уже просмотрена. Дубликаты не учитываются в общей сумме (return 0), но новые строки (return 1).

Ответ 4

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

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');

Ответ 5

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

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. Функция класса может возвращать класс референта. (Я пытался использовать fobject.referent.toString() → , это вернуло много org.netbeans.lib.profiler.heap.InstanceDump. Это также потратило много времени).

Ответ 6

Способ 1

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

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

Способ 2

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