Анализ кучи Java с помощью oql: подсчет уникальных строк
Я делаю анализ памяти существующего программного обеспечения Java. Существует ли sql 'group by', эквивалентный в oql, чтобы увидеть количество объектов с одинаковыми значениями, но разными экземплярами.
выберите количество (*) из группы java.lang.String с помощью s.toString()
Я хотел бы получить список дублированных строк вместе с количеством дубликатов. Цель этого состоит в том, чтобы увидеть случаи с большими числами, чтобы их можно было оптимизировать с помощью String.intern().
Пример:
"foo" 100
"bar" 99
"lazy fox" 50
так далее...
5 ответов
Нижеследующее основано на ответе Питера Долберга и может быть использовано в консоли Visual VM OQL:
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
массив для отслеживания того, какие строки уже включены.
К сожалению, в 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).
Гораздо более эффективный запрос:
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');
Просто опубликуйте моё решение и опыт при выполнении аналогичной проблемы для других ссылок
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. Это также потеряло много моего времени).
Способ 1
Вы можете выбрать все строки, а затем использовать терминал для их агрегирования.
- Увеличьте лимит oql в конфигурационных файлах visual vm
- перезапустить визуальный вм
- oql, чтобы получить все строки
- скопируйте и вставьте их в VIM
- очистить данные с помощью макросов Vim, так что есть
sort | uniq -c
чтобы получить счет.
Способ 2
- Используйте инструмент для сброса всех объектов полей интересующего вас класса ( https://github.com/josephmate/DumpHprofFields может это сделать)
- Используйте bash для выбора интересующих вас строк
- Используйте bash для агрегирования