Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> JProfiler - нахождение "memory leak" 
V
    Опции темы
IDVsbruck
Дата 19.2.2007, 19:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 778
Регистрация: 12.11.2006

Репутация: нет
Всего: 22



У меня достаточно простая программка - парсинг XML и занесение данных в БД.
Размер тестируемого XML - около 8 Мб, суммарно добавляется около 20-22 тыс. записей.
При работе памяти, стандартно выделенной JVR, не хватает. Добавлять параметры в строку запуска не хочу, да и не могу - проект будет раниться на разных машинах в разных местах и размер XML файла неизвестен - от сотен байт до десятков мегабайт, нет гарантии, что даже при большой выделенной памяти он рано или поздно не выдаст OutOfMemoryError.

Вручную перелопатил код взад и вперед - за один проход цикла добавляет суммарно от 10 до 40 Кб. За несколько сотен проходов съедает все.
Практически все вычисления вынес в вызываемые статические методы, но после возврата из них память уменьшается. И хотя это позволило завершить на моей машине работу, но это было как каз впритык ...

Скачал и установил JProfiler. Юзаю, юзаю smile ... но как с помощью него я могу отловить что-то, не очень пойму ... Может, кто даст дельный совет.

Спасибо.
PM MAIL   Вверх
y3u
Дата 19.2.2007, 20:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 440
Регистрация: 9.9.2006
Где: Москва

Репутация: 1
Всего: 13



Попробуй использовать какой-нибудь кеш. Небось все дерево в памяти держишь? smile В профайлере ты можешь посмотреть тенденции на предмет создания инстансов различных объектов. Очень вероятно, о чем писал в биттер джаве Тейт, что у тебя на строки очень много памяти уходит. Значит надо активнее пользоваться константами и не плодить кучи инстансов одинаковых объектов, хоть это и не совсем верно для строк, в виду наличия пула строк... Но, все-таки, я сильно сомневаюсь, что тебе в один момент времени нужно хранить в памяти все дерево, а значит можно, скажем, сериализовать какие-нибудь его куски на время, чтобы не висели в памяти, хотя, это, конечно, несколько скажется на скорости...


--------------------
В нашей стране настаивать на кореньях, черной смородине, лимонных корках - гораздо эффективнее, чем на правах
PM MAIL   Вверх
IDVsbruck
Дата 19.2.2007, 23:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 778
Регистрация: 12.11.2006

Репутация: нет
Всего: 22



Возможно, что все дерево висит в памяти, но тогда все же направшивается вопрос - почему идет утечка памяти?
Путем проверки памяти на каждом этапе выполнения программы выяснил, что утечка как раз идет не при парсинге, а при записи в базу, что особенно странно.
Скажем, в main() вызываю свои методы, которые получают и обрабатывают данные из Документа, и выдают значения. Проверка показала, что память до и после получения равна. Далее вызыватся метод, который заносит данные в БД (упрощено):
Код

private static long setNames(String _f1, String _f2, String _f3)
{
    long result = -1;
    Statement statement = null;
    ResultSet resultSet = null;
    try {
        statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
        resultSet = statement.executeQuery("SELECT * FROM names WHERE company='" + _f1 + "'");
        if (resultSet.next()) {
            result = resultSet.getLong("numid");
            resultSet.updateString("f2", _f2);
            resultSet.updateString("f3", _f3);
            resultSet.updateRow();
        } else {
            String SQLquery = "INSERT INTO names (company, f2, f3) VALUES ('" + _f1 + "', '" + _f2 + "', '" + _f3 + "')";
            int resultQuery = statement.executeUpdate(SQLquery);
            if (resultQuery != 0) {
                ResultSet lastLine = null;
                try {
                    lastLine = statement.executeQuery("SELECT TOP 1 * FROM names ORDER BY numid DESC");
                    lastLine.next();
                    result = lastLine.getLong("numid");
                } finally {
                    lastLine.close();
                }
            }
        }
        resultSet.close();
        statement.close();
    } catch (SQLException ex) {}
    return result;
}

Память съедается именно при обновлениях данных (resultSet.updateString(), resultSet.updateInt() и т.д.) - то есть при каждом апдейте доступная память уменьшается, какой остается при возврате из вызванного метода.
Переменные connection и resultSet пробовал делать как локальными, так и глобальными - результат идентичный ...
PM MAIL   Вверх
w1nd
Дата 19.2.2007, 23:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вертилятор
***


Профиль
Группа: Завсегдатай
Сообщений: 1077
Регистрация: 22.3.2006
Где: Москва

Репутация: 2
Всего: 54



Цитата(y3u @  19.2.2007,  20:48 Найти цитируемый пост)
Значит надо активнее пользоваться константами и не плодить кучи инстансов одинаковых объектов, хоть это и не совсем верно для строк, в виду наличия пула строк...

Именно так. Строки лучше при всяком удобном случае конструировать заново (даже явно).



--------------------
user posted imageuser posted image
PM MAIL ICQ   Вверх
IDVsbruck
Дата 20.2.2007, 01:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 778
Регистрация: 12.11.2006

Репутация: нет
Всего: 22



Строки ... конструировать?
... инстансы ... где они?

Чу-у-уточку попдробнее, я, видимо, вас не сильно понимаю ....
PM MAIL   Вверх
y3u
Дата 20.2.2007, 11:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 440
Регистрация: 9.9.2006
Где: Москва

Репутация: 1
Всего: 13



Цитата(IDVsbruck @  19.2.2007,  23:20 Найти цитируемый пост)
Память съедается именно при обновлениях данных (resultSet.updateString(), resultSet.updateInt() и т.д.) - то есть при каждом апдейте доступная память уменьшается, какой остается при возврате из вызванного метода.
Переменные connection и resultSet пробовал делать как локальными, так и глобальными - результат идентичный ... 


ну, на ум сразу приходит мысль по поводу коннектора. Что за БД, что за коннектор? У меня была ситуация, когда некий коннектор под MSSQL срал нещадно во временную директорию винды - создавал там нереальное количество каких-то временных файлов, всвязи с чем у меня были проблемы и с памятью и с работой с БД - дело решилось заменой коннектора. 


Цитата(IDVsbruck @  20.2.2007,  01:38 Найти цитируемый пост)
Строки ... конструировать?
... инстансы ... где они?


ну это так, были общие мысли... 

ПыСЫ
кстати, для проверки наличия чего бы то ни было, лучше использовать запросы для этого предназначенные SELECT COUNT всякий там... А вместо  "INSERT INTO names (company, f2, f3) VALUES ('" + _f1 + "', '" + _f2 + "', '" + _f3 + "')"; нравственней бы было использовать PreparedStatement


--------------------
В нашей стране настаивать на кореньях, черной смородине, лимонных корках - гораздо эффективнее, чем на правах
PM MAIL   Вверх
IDVsbruck
Дата 20.2.2007, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 778
Регистрация: 12.11.2006

Репутация: нет
Всего: 22



Заремарил все обращения к БД (потери идут не всегда - раз в 3-4 обращения к базе (???) - видимо, действительно, драйвер барахлит), но утечка осталась практически на том же уровне.
Стал построчно проверять память. Это, конечно, дико, но единственное (!!!) место при прохождении по дереву XML, где теряется память - это выражение myNodeList.getLength(), вне зависимости от его расположения - в условиях цикла или при определении интовской переменной. Вот такие вот глюки в пакете org.w3c.dom - это уже стандартными средствами не исправишь!
Одно радует - есть возможность обходить поддерево не через NodeList и его метод getLength(), а через метод Node - getNextSibling() - итерациями.
Возможно, кто-то когда-то сталкивался с подобной "ерундой". Странно, что я нигде не встречал в инете даже упоминания о подобном глюке ...

З.Ы. Драйвер - sun.jdbc.odbc.JdbcOdbcDriver

Это сообщение отредактировал(а) IDVsbruck - 20.2.2007, 16:10
PM MAIL   Вверх
y3u
Дата 20.2.2007, 18:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 440
Регистрация: 9.9.2006
Где: Москва

Репутация: 1
Всего: 13



а ты как, в for цикле небось обходил? скорее всего в этой имплементации - это вычисляемая функция, а ты, небось ее на каждой итерации в for дергал, попробуй либо написать for (int i = 0, j = myNodeList.getLength(); i < j; i++), либо, действительно, возьми другую-нибудь другую имплементацию заточенную для работы с большими XML-никами

ПыСы
А нафига тебе ODBC мост? С любой нормальной БД можно напрямую работать - уже коннекторов развелось полно. Или ты какой-нибудь Access или Excell используешь?

Это сообщение отредактировал(а) y3u - 20.2.2007, 18:31


--------------------
В нашей стране настаивать на кореньях, черной смородине, лимонных корках - гораздо эффективнее, чем на правах
PM MAIL   Вверх
IDVsbruck
Дата 21.2.2007, 12:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 778
Регистрация: 12.11.2006

Репутация: нет
Всего: 22



Вариант for (int i = 0, j = myNodeList.getLength(); i < j; i++) я привел - он так же "кушает", и вообще, любое обращение к getLength() НЕ возвращает память.
Видимо, действительно лучше подключить внешнюю библиотеку, работающую с большими XML-ми. Что наименее громоздко и наиболее удобно?

З.Ы.  smile  smile  smile  Ты совершенно прав - Access. Это не моя прихоть, мне как-то больше по душе PostreSQL, это база заказчика, которой пользуются множество его клиентов. Так что тут выбора, к сожалению, нет.
PM MAIL   Вверх
LSD
Дата 21.2.2007, 13:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 15
Всего: 538



Цитата(IDVsbruck @  21.2.2007,  12:37 Найти цитируемый пост)
Видимо, действительно лучше подключить внешнюю библиотеку, работающую с большими XML-ми. Что наименее громоздко и наиболее удобно?

1. Ты не пробовал обновить JDK?
2. Попробуй Xerces, во всяком случае исходники у него есть и API совпадает с JAXP.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
IDVsbruck
Дата 23.2.2007, 19:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 778
Регистрация: 12.11.2006

Репутация: нет
Всего: 22



Хочу закрыть тему ...
Мои слабые знания на момент возникновения данной темы не позволили мне сразу разобраться в проблеме.

Действительно, дело в DOM парсере и как таковой "утечки памяти" не наблюдается. При парсинге моего 8-ми Мб-тового XML-файла DOM-парсер создал структуру, которая "легла" в памяти на 46 Мб. Так как первоначально в Document находится только структура, то по мере анализа ее и чтения данных они также помещаются в память. Выполнив ~80% работы, DOM-документ стал занимать в памяти ~64 Мб, после чего выдал злостную ошибку OutOfMemoryError. Видимо, это и есть доступная по умолчанию память для проекта, выделяемая JVM.

Единственным не совсем понятным для меня моментом стала такая ситуация: в private методе создаем Документ, парсим в него XML, закрываем поток, обнуляем Документ (null), возвращаемся в вызывающий метод ... а память остается на прежнем уровне. Но, по всей видимости, как об этом пишут источники, это не проблема с проектом, а "неспешная" работа Garbage Collector (Cleaner?) - он просто не успевает до конца выполнения проекта очистить память, так как на нее "пока никто не зарится".

Естественно, проблему решил с помощью технологии SAX - не так просто и наглядно как DOM, но очень шустро и с минимальными затратами памяти. Пик загрузки памяти у меня был ~845 Кб.

Большое спасибо за советы.
PM MAIL   Вверх
LSD
Дата 24.2.2007, 13:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 15
Всего: 538



Цитата(IDVsbruck @  23.2.2007,  19:09 Найти цитируемый пост)
Единственным не совсем понятным для меня моментом стала такая ситуация: в private методе создаем Документ, парсим в него XML, закрываем поток, обнуляем Документ (null), возвращаемся в вызывающий метод ... а память остается на прежнем уровне. Но, по всей видимости, как об этом пишут источники, это не проблема с проектом, а "неспешная" работа Garbage Collector (Cleaner?) - он просто не успевает до конца выполнения проекта очистить память, так как на нее "пока никто не зарится".

GC будет вызываться по мере нобходимости, или его надо вызвать самому System.gc(). Но это не означает, что сразу вся память будет возвращена ОС.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java tools & IDE's | Следующая тема »


 




[ Время генерации скрипта: 0.0568 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.