| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java tools & IDE's > JProfiler - нахождение "memory leak" |
| Автор: IDVsbruck 19.2.2007, 19:31 |
| У меня достаточно простая программка - парсинг XML и занесение данных в БД. Размер тестируемого XML - около 8 Мб, суммарно добавляется около 20-22 тыс. записей. При работе памяти, стандартно выделенной JVR, не хватает. Добавлять параметры в строку запуска не хочу, да и не могу - проект будет раниться на разных машинах в разных местах и размер XML файла неизвестен - от сотен байт до десятков мегабайт, нет гарантии, что даже при большой выделенной памяти он рано или поздно не выдаст OutOfMemoryError. Вручную перелопатил код взад и вперед - за один проход цикла добавляет суммарно от 10 до 40 Кб. За несколько сотен проходов съедает все. Практически все вычисления вынес в вызываемые статические методы, но после возврата из них память уменьшается. И хотя это позволило завершить на моей машине работу, но это было как каз впритык ... Скачал и установил JProfiler. Юзаю, юзаю Спасибо. |
| Автор: y3u 19.2.2007, 20:48 |
| Попробуй использовать какой-нибудь кеш. Небось все дерево в памяти держишь? |
| Автор: IDVsbruck 19.2.2007, 23:20 | ||
| Возможно, что все дерево висит в памяти, но тогда все же направшивается вопрос - почему идет утечка памяти? Путем проверки памяти на каждом этапе выполнения программы выяснил, что утечка как раз идет не при парсинге, а при записи в базу, что особенно странно. Скажем, в main() вызываю свои методы, которые получают и обрабатывают данные из Документа, и выдают значения. Проверка показала, что память до и после получения равна. Далее вызыватся метод, который заносит данные в БД (упрощено):
Память съедается именно при обновлениях данных (resultSet.updateString(), resultSet.updateInt() и т.д.) - то есть при каждом апдейте доступная память уменьшается, какой остается при возврате из вызванного метода. Переменные connection и resultSet пробовал делать как локальными, так и глобальными - результат идентичный ... |
| Автор: IDVsbruck 20.2.2007, 01:38 |
| Строки ... конструировать? ... инстансы ... где они? Чу-у-уточку попдробнее, я, видимо, вас не сильно понимаю .... |
| Автор: y3u 20.2.2007, 11:30 | ||
ну, на ум сразу приходит мысль по поводу коннектора. Что за БД, что за коннектор? У меня была ситуация, когда некий коннектор под MSSQL срал нещадно во временную директорию винды - создавал там нереальное количество каких-то временных файлов, всвязи с чем у меня были проблемы и с памятью и с работой с БД - дело решилось заменой коннектора. ну это так, были общие мысли... ПыСЫ кстати, для проверки наличия чего бы то ни было, лучше использовать запросы для этого предназначенные SELECT COUNT всякий там... А вместо "INSERT INTO names (company, f2, f3) VALUES ('" + _f1 + "', '" + _f2 + "', '" + _f3 + "')"; нравственней бы было использовать PreparedStatement |
| Автор: IDVsbruck 20.2.2007, 15:48 |
| Заремарил все обращения к БД (потери идут не всегда - раз в 3-4 обращения к базе (???) - видимо, действительно, драйвер барахлит), но утечка осталась практически на том же уровне. Стал построчно проверять память. Это, конечно, дико, но единственное (!!!) место при прохождении по дереву XML, где теряется память - это выражение myNodeList.getLength(), вне зависимости от его расположения - в условиях цикла или при определении интовской переменной. Вот такие вот глюки в пакете org.w3c.dom - это уже стандартными средствами не исправишь! Одно радует - есть возможность обходить поддерево не через NodeList и его метод getLength(), а через метод Node - getNextSibling() - итерациями. Возможно, кто-то когда-то сталкивался с подобной "ерундой". Странно, что я нигде не встречал в инете даже упоминания о подобном глюке ... З.Ы. Драйвер - sun.jdbc.odbc.JdbcOdbcDriver |
| Автор: y3u 20.2.2007, 18:28 |
| а ты как, в for цикле небось обходил? скорее всего в этой имплементации - это вычисляемая функция, а ты, небось ее на каждой итерации в for дергал, попробуй либо написать for (int i = 0, j = myNodeList.getLength(); i < j; i++), либо, действительно, возьми другую-нибудь другую имплементацию заточенную для работы с большими XML-никами ПыСы А нафига тебе ODBC мост? С любой нормальной БД можно напрямую работать - уже коннекторов развелось полно. Или ты какой-нибудь Access или Excell используешь? |
| Автор: IDVsbruck 21.2.2007, 12:37 |
| Вариант for (int i = 0, j = myNodeList.getLength(); i < j; i++) я привел - он так же "кушает", и вообще, любое обращение к getLength() НЕ возвращает память. Видимо, действительно лучше подключить внешнюю библиотеку, работающую с большими XML-ми. Что наименее громоздко и наиболее удобно? З.Ы. |
| Автор: LSD 21.2.2007, 13:10 | ||
1. Ты не пробовал обновить JDK? 2. Попробуй Xerces, во всяком случае исходники у него есть и API совпадает с JAXP. |
| Автор: IDVsbruck 23.2.2007, 19:09 |
| Хочу закрыть тему ... Мои слабые знания на момент возникновения данной темы не позволили мне сразу разобраться в проблеме. Действительно, дело в DOM парсере и как таковой "утечки памяти" не наблюдается. При парсинге моего 8-ми Мб-тового XML-файла DOM-парсер создал структуру, которая "легла" в памяти на 46 Мб. Так как первоначально в Document находится только структура, то по мере анализа ее и чтения данных они также помещаются в память. Выполнив ~80% работы, DOM-документ стал занимать в памяти ~64 Мб, после чего выдал злостную ошибку OutOfMemoryError. Видимо, это и есть доступная по умолчанию память для проекта, выделяемая JVM. Единственным не совсем понятным для меня моментом стала такая ситуация: в private методе создаем Документ, парсим в него XML, закрываем поток, обнуляем Документ (null), возвращаемся в вызывающий метод ... а память остается на прежнем уровне. Но, по всей видимости, как об этом пишут источники, это не проблема с проектом, а "неспешная" работа Garbage Collector (Cleaner?) - он просто не успевает до конца выполнения проекта очистить память, так как на нее "пока никто не зарится". Естественно, проблему решил с помощью технологии SAX - не так просто и наглядно как DOM, но очень шустро и с минимальными затратами памяти. Пик загрузки памяти у меня был ~845 Кб. Большое спасибо за советы. |
| Автор: LSD 24.2.2007, 13:40 | ||
GC будет вызываться по мере нобходимости, или его надо вызвать самому System.gc(). Но это не означает, что сразу вся память будет возвращена ОС. |