У Вас когда-либо случались такие ситуации, когда Ваше Java приложение трещит по швам? В моём случае это случилось из-за нехватки доступной оперативной памяти. И, естественно, обнаружилась нехватка в самый неподходящий момент: на носу очередной долгожданный релиз, один из серверов остановлен для обновления кода и данных и реинкарнация старого кода уже невозможна, в ближайшие дни запланировано несколько совещаний и собеседований, что сильно отвлекает от процесса оптимизации - в общем, ЧП не прошло незамеченным.
К слову сказать, сделай я правильный backup и экстренные работы по восстановлению жизнеспособности прошли бы гораздо более спокойно, но это была бы уже совсем другая история. Итак в моём распоряжении есть код, которому не хватает 15Gb оперативной памяти для нормального функционирования и очень длительный и дорогостоящий процесс запуска (около 5 часов), в ходе работы которого можно только сидеть со скрещенными пальцами и надеятся, что в этот раз заветные слова OutOfMemoryError не появятся в консоли удалённого сервера.
Не буду описывать всех ухищрений, которые пришлось проделать, чтобы восстановить остановленный сервер в течении трёх дней, но одним своим мини открытием поделюсь - boolean - это не тот тип данных, который Вы хотите использовать в высоконагруженных системах. Внимание вопрос:
Как Вы думаете, сколько памяти занимает boolean например на Ubuntu server x64?
Правильным ответом будет: неизвестно и зависит только от реализации JVM.
Рассмотрим распространённую Sun JVM и прочтем в спецификации виртуальной машины, что boolean типа в ней нет как такового, вместо него используется int! А это означает, что для хранения значения типа "да\нет" используется ровно 32 бита, независимо от архитектуры процессора. Правда в том же разделе мы видим, что произведена оптимизация для работы с массивами boolean, которые преобразуются в массив байт, что даёт прирост доступной памяти в 4 раза. И всё же платить за хранение нолика или еденички семью лишними битами - иногда просто кощунство и издевательство над серверами (особенно при размерах массивов в 500 миллионов элементов).
Спасением в таких случаях будет класс BitSet, который ведёт себя подобно массиву boolean, но упаковывает данные так, что для одного бита выделяется всего один бит памяти (с небольшими издержками для всего массива). BitSet хранит внутри себя массив типа long, а при запросе или установке значения определенного бита - высчитывает индекс нужного long и пользуясь побитовыми операциями и операциями сдвига производит вычисления над единственным битом.
Существует еще более интересная реализация BitSet, OpenBitSet - Apache реализация, которая используется для Lucene. Она гораздо быстрее, но упускает некоторые проверки, проводимые в оригинальном BitSet. Что использовать - решать Вам.
ИМХО такие статьи лучше писал бы на спец ресурсах (хоть на хабре). И больше бы людей посмотрело, и интересные отзывы можно получить.
ReplyDeleteВо-первых данный блог подразумевался как технический или около технический, во-вторых долго упрашивать меня не нужно - запостил статью в песочницу Хабра.
ReplyDeleteА вообще и здесь есть люди, которым будет это интересно.
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/OutOfMemoryError.html
ReplyDeleteOutOfMemoryError потому и Error, а не Exception, что является unrecoverable, т.е. это JVM crash. Кстати, очень популярный вопрос на собеседованиях.
P.S. Читал на Хабре, там аккаунта не имею - поэтому комментирую тут.
Да, виноват, согласен.
ReplyDeleteЭто не мешает ему вызывать исключительную ситуацию, но поправить стоит.
Видел статью на Хабре.
ReplyDeleteЗанимаюсь похожими задачами - парсеры больших текстовых файлов (~1 GB) на java. Предидущие парсеры работали по 5-10 часов. Оптимизированные варианты работают считанные минуты - за счет оптимизации работы с диском, построения индексов, распараллеливание чтения, оптимизации структур хранения.
1. Для экономии памяти не пробовали ли использовать CompressedOops?
http://wikis.sun.com/display/HotSpotInternals/CompressedOops
2. Так же можно упаковывать данные быстрыми пакерами типа http://www.quicklz.com
Алексей.
Мои файлы в разы больше. В запакованном .bz2 состоянии порядка 10Gb и растут.
ReplyDeleteЗа ссылки спасибо - поштудируем.
Бодрые объемы - интересные задачи! Ну тогда похоже на затык IO, напрашивается распараллеливание процессинга :)
ReplyDeleteЕще можно GC потюнить, на таких объемах возможны неприятные паузы:
ReplyDeletehttp://gregluck.com/blog/archives/2006/07/how-we-solved-our-garbage-collection-pausing-problem/
Да Вы просто кладязь полезных ссылок. Спасибо большое :)
ReplyDeleteДля ускорения (обещают 2-5 раз :) XML парсера можно попробовать следущее:
ReplyDeletehttp://javolution.org/target/site/apidocs/javolution/xml/sax/SAX2ReaderImpl.html
ссылка на бенчмарки:
http://www.xml.com/lpt/a/1702
Алексей, Вам аккаунт на Хабре нужен? Как только появится возможность - сразу скину Вам :)
ReplyDeleteСпасибо, от аккаунта не откажусь :)
ReplyDeleteДа, еще, просто как идея распараллеливания обработки - http://www.hazelcast.com/ для Distributed java.util.{Queue, Set, List, Map} :)
ReplyDeleteПо поводу космических объемов данных в 10 GB в архиве. Я как понимаю речь идет о planet-latest.osm.bz2 (openstreenmap xml формате)? Насколько я заметил, то там есть инкрементные дневные апдейты вменяемых размеров (десятки MB) или напиленные на континеты и страны фрагменты:
ReplyDeletehttp://downloads.cloudmade.com/, если все-таки уперлость заливать целым куском :)
Спасибо, но про OSM я знаю больше, чем Вам кажеться :-)
ReplyDeleteНапилением на страны, с некоторых пор занимаюсь тоже я ;)
А в данном, конкретном случае мне нужен весь файл планеты целиком. Иначе, к сожалению, пока не получается.
А, Семен Семенович (с), теперь все встало на свои места!
ReplyDeletehttp://planet.openstreetmap.org/ +
http://algo2.iti.kit.edu/routeplanning.php =
http://developers.cloudmade.com/projects/show/routing-http-api :)
Вот и познакомились :-)
ReplyDeleteОбещание инвайта в силе - как только появиться: отпишусь.