Накипело #2. XML.
20.10.2008
Ну, как я и обещал, пришло время рассказать еще об одной «состоявшейся технологии», которую лично я терпеть не могу. Думаю, вы поняли из названия, что речь пойдет про XML.
Итак, что такое XML? Это язык разметки документов. Точка. Имею ли я против «расширяемого языка разметки»? Нет. Если он применяется именно для того, для чего он предназначен.
К сожалению, куча людей считает, что XML — это круто. Это, сука, крайне удобная и надежная технология. Так давайте же пихать его везде, куда можно и нельзя! Давайте пихать его в API сервисов, в конфигурационные файлы, сделаем его «стандартом» обмена данными между сервером и клиентом (в случае RIA).
Знаете, что я думаю? Я думаю, что XML повсеместно внедрили какие-то человеконенавистники. По прямому заказу какого-нибудь Люцифера. Ага.
Почему я так думаю? Потому что, блядь, тяжело придумать более тупую хуйню, чем XML в качестве формата хранения и передачи данных. Блядь, любой другой формат будет в тысячу раз лучше!
XML предназначен для разметки документов. Документов, сука. Которые обрабатывает специальная программа, а потом выводит в красивом и понятном человеку виде. Нахуя использовать XML в передаче данных лично мне непонятно, лишние телодвижения ради лишних телодвижений: берем данные, превращаем их в документ, передаем на клиент, превращаем документ в данные и пользуемся ими. Охуеть. Не проще ли взять данные, передать их и использовать данные на клиенте? Наверное, проще. Наверное, именно поэтому JSON вытесняет XML.
Те, кто работал с java-фреймворками, поймут меня — XML это зло. И чем больше XML'a в проекте, тем больше зла. Особенно отличились на поприще распространения зла appfuse и tapestry. Ну, java вообще в этом смысле отличный язык — в смысле поебаться на пустом месте.
Я так понимаю, началось все с того, что какой-то идиот придумал, что XML легко читать человеку. И легко править. Ебанаврот! Это чисто машинно-ориентированный формат, и чтобы его прочитать, надо пять раз уебаться башкой об стену, чтобы голова напоминала по форме системный блок — вот тогда, наверное, можно будет читать этот «документ» свободно.
Или он подумал, что круто будет задавать property в виде XML, потому что так можно в сам формат зашить, какие именно свойства можно объявлять и какие они могут иметь значения. В данном случае, по моему мнению, нужно немножко больше верить в человека. Наверное, не совсем уебаны работают на должностях программистов и администраторов, и уж как-нибудь прочитают документацию, перед тем как что-то писать в конфиги. А не прочитают — ну так и XML им не поможет, это клиника. Лично я думаю, что гораздо проще и естественнее писать конфиги в формате .property:
названиеСвойства = значениеСвойстваИли посмотрите в сторону YAML, который используется в рельсах — руки от зависти можно сгрызть, как все просто и понятно.
Как бы то ни было, мы, java-программисты, имеем то, что имеем. Уебанские, нечитаемые и сложные в написании конфигурационные файлы.
К счастью, ситуация начинает меняться в лучшую сторону, и здравый смысл побеждает — XML постепенно уступает место более вменяемым форматам.
Резюмируя: я не против XML. Это отличная технология, которая хороша на своем месте. Но. Я считаю, что затыкать каждую дырку XML смешно и глупо. И поэтому использование XML надо ограничить.
Накипело. RegExp.
19.10.2008
Я считаю, что регулярные выражения надо законодательно запретить. Наряду с xml. Хотя нет, xml не нужно запрещать, надо только ограничить область его использования. Но об этом я как-нибудь в другой раз напишу. А пока вернемся к регулярным выражениям.
Взгляните, к примеру, на это уебище:
/(^|[\n ])((www|ftp)\.[\w\#$%&~/.\-;:=?@\[\]+]*)/is
Ни один, сука, человек в здравом уме не сможет сказать с первого взгляда, что делает эта поебень, и, главное, правильно ли она это свое дело делает. Надо, блять, копаться, посимвольно разбирая, что ловит каждая группа, чтобы только понять, что это регулярное выражение находит в тексте url и разбирает его на части.
Но это еще хуйня. Самый секс с regexp'ами — отладка. Блядь, у меня в башке звенит, когда я пытаюсь понять, что в этой каше надо изменить, чтобы эта хуйня работала правильно. Кстати, приведенный пример бажный — он не учитывает, что в строке параметров может быть запятая.
Еще охуенно можно поебаться с поддержкой кода, написанного другими людьми — после отладки вот таких вот иероглифов как никогда проникаешься любовью к человечеству вообще и коллегам в частности.
Короче. Программирование — тяжелая штука. Лучшие умы бьются над тем, чтобы упростить написание программ, чтобы минимизировать затраты времени на получение работающего, безбажного, легкоподдерживаемого и расширяемого кода. В принципе, все, что происходит в мире программирования (и всякие там. ООП, и паттерны проектирования, и всякие гибкие методики разработки, и новые языки, и фреймворки) нацелено на решение именно этой задачи — «Как бы нам так изъебнуться, чтобы поменьше ебаться?». И я искренне надеюсь, что в какую-нибудь светлую голову (вроде моей:) стукнет идея, чем можно заменить, не потеряв в мощности и гибкости, регулярные выражения. А для того, чтобы эта идея поскорее катапультировалась с далекой звезды и ебнула кого-нибудь по темечку я предлагаю пока что законодательно ограничить использование regexp'ов — ну там, к примеру, не больше одной регулярки на приложение — если больше, то плати штраф. Если клиент настаивает — пускай закладывает в бюджет размер штрафа. Как-то так, рублем подгонять исследовательскую мысль.
Ускоряем wordpress
11.10.2008
Привет. Думаю, среди читателей моего блога немало тех, кто имеет stand-alone blog на движке wordpress.
Так вот, для вас, дорогие мои, у меня есть две новости, как водится, плохая и хорошая. Плохая состоит в том, что wordpress — довольно-таки тормознутая штука. Виноваты в этом в основном криворукие производители тем и, особенно, криворукие производители плагинов.
особенно кривой плагин, на мой вкус, wp-ajax-edit-comments, который является образцом быдлокодинга
Хорошая — в том, что это можно поправить. Сейчас дядя Саша научит вас, как.
Теория
Сначала немного теории. Уже довольно давно умные люди из компании Yahoo провели исследования на тему «как же нам ускорить наши сайты». И выяснили, что на скорость сайта с точки зрения пользователя в основном влияет оптимизация front-end'a, а не server-side. Подробнее об этом можно почитать на сайте webo.in (кстати, отличный ресурс, посвященный оптимизации времени загрузки веб-сайтов) на русском и на сайте yahoo на английском, я же просто опишу несколько простых шагов, которые позволят существенно ускорить скорость работы своего блога.
Хотя я и старался максимально упростить текст, сведя его к набору инструкций, все же большинство шагов можно выполнить только имея опыт разработки веб-сайтов, так что, если вы не программист, то лучше попросите знакомого программиста выполнить эти шаги за вас. Да, и на всякий случай, не забудьте забэкапиться:)
Перед тем, как мы перейдем к практике, я напомню нашу основную цель: блог на движке wordpress должен работать с точно тем же функционалом, что и раньше, но, с точки зрения пользователя, работать быстрее. Итак, погнали:
Практика
Оптимизируем тему
Да, открытость платформы Wordpress — это очевидное благо. Я серьезно. Множество прекрасных дизайнеров, верстальщиков, программистов с горящими от энтузиазма глазами вдохновенно, вдумчиво создают темы и плагины для всех, для всего человечества, не требуя ничего взамен. Это действительно прекрасно.
К сожалению, не обошлось и без ложки дегтя — далеко не все верстальщики и программисты одинаково опытны и талантливы и далеко не все они в достаточной степени усердны и ответственны, чтобы на общественно-добровольных началах вылизать тему, которую они предоставляют.
Еще большее сожаление прогрессивно мыслящей части человечества вызывает тот факт, что уебанов больше, чем титанов духа, и в связи с этим сеть полна бесплатного говна.
Таким образом, нам надо исправить недостатки, которые (возможно) присущи вашей любимой теме от рождения и поправить ее код.
Итак, для того, чтобы тема стала работать быстрее, надо сделать следующее:
- Если тема сверстана на таблицах, переверстать ее на дивы. Я не буду касаться давнего спора «как вестать — дивами и таблицами», замечу только, что точки зрения поставленной перед нами цели (быстро работающий с точки зрения пользователя блог) таблицы проигрывают дивам — потому что таблица отрисовывается браузером только после того, как будет полностью загружена, тогда как дивы отрисовываются сразу, как только браузер получит их с сервера. А значит, если страница сверстана в дивах, пользователь быстрее увидит контент сайта, что нам и нужно, не так ли?
- Вы будет смеяться, но нужно убрать все стилевые правила во внешние файлы. И JavaScript — тоже. Это настолько очевидно, что я даже не буду пояснять, зачем это нужно.
- Внешний стилевой файл прописываем в блоке head, а JavaScript файл подключаем как можно ближе к закрывающему тэгу «body». И все скрипты аналитиков тоже располагаем пониже.
- Сжимаем js и css. Для этого мы будем использовать yui-comressor. Делается это примерно так: качаем последний стабильный релиз YUICompressor'a с официального сайта, устанавливаем, если еще не установлено JRE и выполняем на css/js файл команду следующего вида:
java -jar /path/to/yuicompressor-*.*.*.jar -o "output_filename" $file
можно использовать флаг -type, который указывает, какой тип файла. Если флаг не указан, то тип файла определяется по расширению.
Можете использовать тему моего блога как пример.
Уменьшаем количество файлов
Так как мы можем серьезно ускорить скорость загрузки файлов, уменьшив число этих файлов (удивительно, правда?:), то разумнее всего будет слить все css-файлы и js-файлы в один. Если эти файлы размером больше ~70 килобайт, то лучше разбить их на два куска.
Если нужно уменьшить количество картинок, используя технику css спрайтов и технику image map.
Тут надо отдельно заметить, что у многих (практически у всех) плагинов есть совершенно идиотская особенность — прописывать свои js и css файлы. Идиотизм заключается в том, что очень часто разные плагины используют одну и ту же, скажем библиотеку, и подключают ее по нескольку раз. И пользователь, просматривающий ваш блог, вынужден по два-три раза грузить, к примеру, prototype или jquery. Лишние ~30–160 KB. Неслабо, прадва?
Тех. заметка: при этом совершенно непонятно, что мешало создателям wordpress сделать контроль надо всеми ресурсами, что прописывают плагины. К примеру, если один плагин, которому нужен jQuery, прописывает тэг script с jQuery и рапортует — «Вот мол, подгрузил, все, кому надо, могут использовать», а другой, которому тоже нужен jQuery уже знает об этом и не подгружает свой вариант
Лечится это так — все скрипты, что подгружают плагины слейте с теми, что написали сами и воткните в футер, а плагинам запретите их подгружать. Тоже самое сделайте с css, только воткните в header.
Оптимизируем графику
Картинки в png и jpg форматах довольно часто неоптимизированы и хранят много лишней информации. Было бы неплохо избавиться от этой лишней информации и таким образом сжать файлы. Делается это с помощью специальных утилит. Это позволит нам уменьшить их размер в отдельных случаях на 50 процентов. Неслабо, правда?
При этом сами картинки не изменятся и все так же будут радовать глаз пользователей.
Информацию об этих утилитах и команду я позаимствовал у Дмитрия Ищенко. Надеюсь, он не в обиде:)
Итак, для оптимизации всего png картинок мы будем использовать pngcrush. Я затрудняюсь ответить, как ее инсталлировать на windows или linux, но я на своем mac'e без проблем установил эту утилиту из портов.
Чтобы сжать png-файлы без потери качества, используйте следующую команду:
pngcrush -rem alla -reduce -brute image.png result.png
Для сжатия jp (e)g-файлов используйте jpegtran, которая входит в пакет libjpg, который я также установил из портов. Команда для сжатия jp (e)g-файлов без потери качества:
jpegtran -copy none -optimize -perfect src.jpg dest.jpg
Я тут набросал shell-скрипт, который рекурсивно пройдет по директории и оптимизирует все png/jp (e)g картинки в ней:
for file in `find . -iname "*.jpg" -or -iname "*.png" -or -iname "*.jpeg"`;do ext=${file##*.} if [ -n "$ext" ]; then if [ "$ext" = "jpg" ]; then echo "optimizing ${file} as jpeg file with jpegtran" jpegtran -copy none -optimize -perfect -outfile temp_abracadabra_filename.jpg $file mv -f temp_abracadabra_filename.jpg $file; fi if [ "$ext" = "jpeg" ]; then echo "optimizing ${file} as jpeg file with jpegtran" jpegtran -copy none -optimize -perfect -outfile temp_abracadabra_filename.jpeg $file mv -f temp_abracadabra_filename.jpeg $file; fi if [ "$ext" = "png" ]; then echo "optimizing ${file} as png file with pngcrush" pngcrush -rem alla -reduce -brute "$file" temp_abracadabra_filename.png; mv -f temp_abracadabra_filename.png $file; fi fi done;
Просто выполните его в директории uploads что в папке wp-content и все будет хорошо. Можно даже повесить его на cron-job и больше не париться на этот счет — все вновь прибывшие файлы будут оптимизироваться.
Разумеется, надо оптимизировать графику, используемую в теме, так что вот еще один shell-скрипт, который рекурсивно пройдет по директории, сожмет css, js файлы и оптимизирует jp (e)g/png файлы. На всякий случай перед его использованием не забудьте забэкапиться:
for file in `find . -iname "*.jpg" -or -iname "*.jpeg" -or -iname "*.png" -or -iname "*.js" -or -iname "*.css" `;do ext=${file##*.} if [ -n "$ext" ]; then if [ "$ext" = "css" ]; then echo "compressing ${file} as css file with yui compressor" java -jar /opt/yuicompressor/yuicompressor-2.3.5.jar --type css -o "temp_abracadabra_filename.css" $file mv -f temp_abracadabra_filename.css $file; fi if [ "$ext" = "js" ]; then echo "compressing ${file} as js file with yui compressor" java -jar /opt/yuicompressor/yuicompressor-2.3.5.jar --type js -o "temp_abracadabra_filename.js" $file mv -f temp_abracadabra_filename.js $file; fi if [ "$ext" = "jpg" ]; then echo "optimizing ${file} as jpeg file with jpegtran" jpegtran -copy none -optimize -perfect -outfile temp_abracadabra_filename.jpg $file mv -f temp_abracadabra_filename.jpg $file; fi if [ "$ext" = "jpeg" ]; then echo "optimizing ${file} as jpeg file with jpegtran" jpegtran -copy none -optimize -perfect -outfile temp_abracadabra_filename.jpeg $file mv -f temp_abracadabra_filename.jpeg $file; fi if [ "$ext" = "png" ]; then echo "optimizing ${file} as png file with pngcrush" pngcrush -rem alla -reduce -brute "$file" temp_abracadabra_filename.png; mv -f temp_abracadabra_filename.png $file; fi fi done;
Используйте меньше dns-lookups. Не выкладывайте картинки, src которых указывает на другой ресурс, лучше загрузите их к себе и пропишите ссылку на автора. Работать будет быстрее.
Сжатие
Все современные браузеры поддерживают сжатие, так что можно существенно уменьшить размер отдаваемых файлов (а значит, и время их загрузки) при помощи moddeflate (для Apache 2.2 для Apache 1.3 надо использовать modgzip). Так что включите moddeflate. Если же вы используете Apache 1.3, ниже приведенный код вам не не нужен, вам поможет статья »modgzip — сжатие html страниц 'на лету'» на сайте webo.in.
Найдите файл .htaccess в корневой директории установки wordpress и добавьте в конец следующее:
<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml SetOutputFilter DEFLATE BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html SetEnvIfNoCase Request_URI \.(?:gif|png)$ no-gzip dont-vary Header append Vary User-Agent env=!dont-vary </IfModule>
Таким способом можно добиться уменьшения js/css/html файлов на 70–80%, и примерно 10% уменьшения jpeg-файлов, что значительно ускоряет загрузку сайта. Следует, правда, помнить, что использования moddeflate увеличивает нагрузку на сервер, так как ему нужно сжать файлы перед тем, как отдать их, так что стоит проконтролировать, что использование moddeflate не создает чрезмерной нагрузки на сервер.
Кеширование
Ну и последнее — для того, чтобы ускорить серфинг по вашему сайта нужно врубить кеширование. Это не ускорит загрузку сайта у пользователя, который первый раз пришел на ваш сайт, но положит все внешние js, css файлы, картинки к нему в кеш, и в следующий раз, когда он будет бродить по вашему сайту, ресурсы будут грузиться не с сервера, а из кеша, что значительно ускорит скорость работы вашего сайта с точки зрения пользователя, а также значительно снизит нагрузку на ваш сервер. Помните, что закешированные у пользователя файлы берутся из кеша браузера, поэтому, если вы внесете изменения в файл, скажем, стилей, пользователь, закешировавший style.css, не увидит их. Так что лучше включать кеширование после того, как вы доработали тему.
Опять же, добавьте следующие строчки в конец файла .htaccess и не забудьте включить modheaders и modexpires (или хотя бы один из них):
# используем mod_expires <IfModule mod_expires.c> ExpiresActive On ExpiresDefault A86400 ExpiresByType image/x-icon A2592000 ExpiresByType application/x-javascript A2592000 ExpiresByType text/css A2592000 ExpiresByType image/gif A604800 ExpiresByType image/png A604800 ExpiresByType image/jpeg A604800 ExpiresByType text/plain A604800 ExpiresByType application/x-shockwave-flash A604800 ExpiresByType video/x-flv A604800 ExpiresByType application/pdf A604800 ExpiresByType text/html A900 </IfModule>
# используем mod_header <IfModule mod_header.c> # 3 Month <FilesMatch "\.(flv|gif|jpg|jpeg|png|ico|swf)$"> Header set Cache-Control "max-age=7257600" </FilesMatch> # 1 Week <FilesMatch "\.(js|css|pdf|txt)$"> Header set Cache-Control "max-age=604800" </FilesMatch> # 10 Minutes <FilesMatch "\.(html|htm)$"> Header set Cache-Control "max-age=600" </FilesMatch> # NONE <FilesMatch "\.(pl|php|cgi|spl)$"> Header unset Cache-Control Header unset Expires Header unset Last-Modified FileETag None Header unset Pragma </FilesMatch> </IfModule>
Кеширование на стороне сервера
Есть несколько плагинов к wordpress'у, которые позволяют кешировать файлы на стороне сервера. Работают они по такому принципу: как только какая-то из ваших страниц запрашвается на сервере, она динамически строится и создается html-файл со всеми данными, который ложится в кеш. Как только приходит запрос на эту страницы, пользователю отдается html файл вместо того, чтобы динамически строить страничку, что значительно снижает нагрузку на сервер (теперь ведь не прогоняются php-скрипты и не нагружается база данных). К сожалению, все эти плагины работают как-то очень странно, во всяком случае, у меня не работали должным образом ни wp-cache, ни работающий на его основе wp-super-cache. Зато работает 1 Blog Cacher, правда, у него достаточно сложная настройка. Надеюсь, вы разберетесь. Я использую его.
Итого
После того, как я провел над своим блогом ряд этих нехитрых действий, блог стал загружаться на 80% быстрее. Теперь perfomance meter моего сайта с точки зрения плагина к firebug'у YSlow равен B (85), был F (33). Думаю, неплохой результат.
Ваш JBoss AS еще не летает? Тогда мы идем к вам:)
11.09.2008
Я забыл рассказать еще про одну фишку, которая ускорит работу вашего JBAS'a (JBAS == JBoss AS).
Большинство людей не знает, что такое RMI DGC. И большинству людей не нужно знать, что такое RMI DGC. Это я к тому, что мы с вами не входим в это большинство, так что по ссылке сходить стоит. Благо, текста там немного.
Вне зависимости от того, используете ли вы в своем коде RMI или нет, JBAS подгружает подсистему RMI. По умолчанию, подсистема RMI запускает принудительную очистку мусора один раз в минуту. Это означает, что все потоки ставятся на паузу и проводится полное сканирование кучи. Это занимает довольно много времени, особенно, если сервер сильно нагружен.
Короче, чтобы JBOSS работал побыстрее, вставь эту строку в run.conf (который, напоминаю, расположен в $JBOSS_HOME/bin) куда-нибудь после того IF-a, что мы изменяли в предыдущей статье:
JAVA_OPTS="$JAVA_OPTS -Dsun.rmi.dgc.client.gcInterval=3600000 \ -Dsun.rmi.dgc.server.gcInterval=3600000"
(Обратный слеш \ в данном случае не имеет никакого значения, он просто экранирует перевод каретки, чтобы строка кода влезла в формат поста. Когда будешь вставлять эту строку, можешь просто срастить эти две строки в одну, а слеш удалить.)
Есть еще один вариант. Можно запретить любому коду (в том числе и RMI подсистеме) запускать принудительную очистку мусора, так что вместо вышеуказанных опций можно указать XX:+DisableExplicitGC. В этом случае сборка мусора будет запускаться если куча будет заполнена примерно на 70% (это дефолтное значение и оно тоже настраивается).
Какой из двух вариантов использовать? Не знаю, пока тестирую оба, посмотрим, что получится.
Возникает вопрос, а когда нужно это поведение-по-умолчанию RMI DGC? Никогда. Конечно, если твой код выполняет кучу вызовов удаленных объектов (remote object calls) (EJB, привет!:) и при этом не кэширует ваши LOCAL или REMOTE объекты, то тогда, возможно, очистки мусора раз в час будет недостаточно.
Те, кто хотят узнать побольше про управлению памятью, могут почитать вот эти мои статьи.
Java. Сборщики мусора. Часть вторая.
09.07.2008
Это продолжение вчерашнего поста про сборщики мусора в JavaTM. Напоминаю, что все эту бодягу я начал с целью разобраться, что такое ошибка PermGenSpace: OutOfMemory exception.
Прежде чем рассказать о том, как работает сборка мусора в современной JVM (1.4, 1.5), давайте поговорим о поколениях объектов.
В динамической памяти приложения некоторые объекты становятся мусором вскоре после создания, некоторые живут в течение долгого времени и только затем становятся мусором, другие могут остаться в живых в течение всего времени работы программы. Как показывает практика, в большинстве объектно-ориентированных языков, включая Java, огромное количество объектов, приблизительно 98 % , умирают молодыми. Возраст объектов можно измерять в секундах настенных часов, в общем количестве байтов, выделенных подсистемой управления памятью, с тех пор, как объект был размещен, или в числе сборок мусора с момента размещения объекта. Но как бы Вы не измеряли, большинство исследований показывает, что объекты умирают молодыми.
Тут я даже постарался и с помощью Google Chart Creator нарисовал график:
Так как мы живем в неидеальном мире, у каждого из этих алгоритмов есть как преимущества, так и недостатки. Например, копирование хорошо работает, когда большой процент объектов является мусором (потому что этот вид сборщиков мусора не посещает мертвые объкты — он просто копирует живые объекты), но плохо работает с большим количеством долгоживущих объектов (периодически выполняя их копирование). Наоборот, маркировка-сжатие вполне хорошо работает с долгоживущими объектами (копируя их только один раз), но не так хорошо с большим количеством объектов, живущих недолго.
Теперь, зная это, можно сделать вывод, что лучшим вариантом сборки мусора было бы использование копирующего сборщика мусора, а для долгоживущих — маркирующе-сжимающего.
На основе подобных рассуждений, в Java был введен метод сборки мусора, берущий лучшее от обоих алгоритмов и нивелирующих их недостатки: так называемая «основанная на поколениях сборка мусора». Дополнительно он обеспечивает очень низкие затраты на размещение объектов.
Сборка мусора на основе поколений

Сборщик мусора, основанный на поколениях, делит динамическую память на несколько поколений — youth (молодые), tenured (Дословно — выжившие. На мой взгляд, очень поэтично:), perm — вечные. Youth дополнително делится на область генерации объектов Eden (совсем поэтично) и две полузоны для работы копирующего сборщика. Я еще не говорил. Объекты, отвечающие некоторым критериям продвижения, например, они пережили определенное число сборок мусора, затем переводятся в более старшее поколение — сначала Tenured (убирается маркирующе-сжимающим сборщиком мусора), затем и Perm (вообще не убирается).
Одно из преимуществ сборки мусора на основе поколений заключается в том, что она может сократить паузы, не обрабатывая все поколения сразу. Если распределитель ресурсов не способен выполнить запросы на размещение, то он сначала запускает вспомогательные сборки мусора, которые обрабатывают только самое младшее поколение. Так как многие объекты в молодом поколении будут уже мертвы, а копирующему сборщику мусора совсем не надо проверять мертвые объекты, то паузы на вспомогательные сборки мусора могут быть достаточно малыми, и зачастую могут возвращать значительный объем динамической памяти. Если вспомогательная сборка мусора освобождает достаточно места в динамической памяти, то мутатор может немедленно возобновить работу. Если же она освобождает недостаточно места в динамической памяти, то сборка продолжится в более старших поколениях до тех пор, пока не будет возвращен достаточный объем памяти. (В случае если сборщик мусора не может возвратить достаточный объем памяти после полной сборки мусора, то он или расширит динамическую память, или выдаст OutOfMemoryError.)
В принципе, уже из этого понятно, что такое PermGen, и почему у него заканчивается место при redeploy'е. Желающие узнать больше о принципах работы поколенного сборщика мусора могут обратиться к еще одной статье Брайна Гетца «Сборка мусора в HotSpot JVM».
Отдельно стоит упомянуть про многопроцессорные системы. В ранних версиях JDK сборка мусора в параллельных потоках сильно замедляла работу систему. Я даже спиздел из статьи Tuning Collection with the 5.0 Java[tm] Virtual Machine классную картинку, которая визуализирует зависимость между временем сборки мусора и количеством процессоров:
К счастью, в JDK 1.4 добавили специально для таких систем еще три сборщика мусора: параллельный копирующий сборщик мусора, параллельный утилизирующий сборщик мусора и синхронизированный маркирующе-чистящий сборщик. Эти новые сборщики призваны решить проблему сборки мусора, которая является основным припятствием для решения проблемы масштабируемости на многопроцессорных системах.
Ну и напоследок еще одна пизженная у Брайна Гетца картинка, на которой проиллюстрированы рекомендации для выбора сборщика мусора:
Настройка JBOSS AS
PermGen Space: OutOfMemory exception вылетает от того, что оставшиеся от предыдущего deploy'я вечный объекты так и остаются в PermGen (это я не знаю, а предполагаю — и могу ошибаться) и PermGen Space рано или поздно переполняется. Соответственно, для решения этой проблемы нам надо как-то заставить сборщик мусора убирать более не нужные объекты в PermGen.
На практике это выглядит так:
- открываем файл run.conf в папке bin jboss'a, находим строку, начинающуюся с JAVA_OPTS и добавляем в нее следующие параметры:
- Разрешаем сборщику мусора убирать PermGen:
-XX:+CMSPermGenSweepingEnabled - Так как JVM так просто не отдаст на растерзание Perm объекты, надо умилостивить ее:
-XX:+CMSClassUnloadingEnabled - Для того, чтобы сборщик мусора не прибил нужные нам объекты, нам нужно увеличить размер PermGen:
-XX: MaxPermSize=128m - (Опционально) Если jboss вертится на многопроцессорной системе, то не забудьте включить синхронизированный сборщик мусора:
-XX:+UseConcMarkSweepGC
Для удобства использования выложу мою строку JAVA_OPTS:
JAVA_OPTS="-Xms256m -Xmx1024m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=128m"
Java. Сборщики мусора. Часть первая.
08.07.2008
Честно признаюсь — я жутко ленив. Я не люблю копаться в чем-то, добиваясь наилучшего быстродействия. Я могу, к примеру, полгода перешагивать через провод удлинителя, протянутый через всю комнату, пока однажды, споткнувшись, не оторву его нахуй. Вот после этого я наконец-то озадачусь протягиванием нормальной проводки до нужного угла комнаты или сделаю перестановку. Но не раньше.
Примерно так же я относился до недавнего времени к регулярно вылетающим PermGen: OutOfMemory exception от JBOSS Application Server на моем ноуте. Ну, бывает иногда, и хуй с ним.
Пока однажды я не заметил, что перезапускаю сервер по три-четыре раза в час, теряя на этом минут пять. Пять минут в час, если кто не понял — это 730 часов в год или 30 дней, или месяц. Месяц тупого пяленья в мелькающие в консоли строки загрузки сервера и initial deploy'я проекта! Блядь!
И я решил раскопать, что именно вызывает этот самый PermGen: OutOfMemory exception. Чтобы, разумеется, «протянуть нормальную проводку».
Прежде чем начинать разговор о том, что такое PermGen, и почему в нем заканчивается место, а также как это поправить, я расскажу на пальцах, как JavaTM работает с памятью, что такое сборщики мусора, какие виды сборщиков мусора бывают и как они работают в современных JVM.
I. Общие слова
Так уж исторически сложилось, что в отношении работы с памятью Java сравнивают с c++. Корни такого подхода лежат еще в тех временах, когда Java нигде толком не использовалась, но уже активно рекламировалась и им нужно было как-то перетягивать разработчиков с c++.
В c++ в общем случае выделение памяти происходит явно с помощью явно написанного конструктора, в котором разработчик часто вручную управляет выделением памяти с помощью операторов malloc/calloc, причем разработчику надо явно проинициализировать переменные, с которыми он работает. Удаление объекта производится вызовом деструктора, который также пишется вручную, и лучше бы ему не забыть очистить всю память, которую он выделил под объект, иначе он замудохается искать, куда утекает память приложения.
В JavaTM возлагает на виртуальную машину управление памятью — выделение памяти под объекты, удаление объектов, освобождение памяти. В том же самом общем случае объекты хранятся в куче, а не в стеке (за исключением примитивных типов), что увеличивает время выделение памяти и доступа к объекту. Зато JVM их 100% проинициализирует, так что вы не получите -44441222, обратившись к непроинициалированному явно int'у.
Во-вторых, удалением объекта и освобождением памяти занимается не вручную написанный деструктор, а сборщик мусора (garbage collector), работающий в параллельном потоке. Что, с одной стороны, очень хорошо — теперь программисту не нужно думать о том, прибрался ли он за собой, а также о том, где взять память, но, с другой стороны, неизвестно, когда именно сборщик мусора утилизует объект и отдаст свободную память. Не говоря уж о том, что сборщик мусора тоже требует для своей работы системных ресурсов.
II. Выделение памяти
Прежде чем переходить к сборщикам мусора, не лишним будет напомнить, как происходит выделение памяти под объекты:
Рассмотрим простейший пример:
public class FuckingExample { int x; } FuckingExample boolShit = new FuckingExample();
С помощью оператора new мы можем создать новый объект. При этом JVM самостоятельно выделит память под объект и проинициализирует его поля (в данном примере — x). В отличии от C++ в Java не нужно писать конструктор по умолчанию — JVM в случае его отсутствия самостоятельно справится с инициализацией объекта.
В отличии от все того же многострадального C++ не нужно явно инициализировать поля с примитивными типами — JVM дает гарантию, что присвоит переменной примитивного типа значение этого типа по умолчанию. Другими словами, вы можете быть уверенными, что в случае, если вы явно не проинициалируете переменную, как в примере, то значение x будет равно 0, а не мусору, как было бы в C++.
Впрочем, не все объекты располагаются в куче. Для примитивных типов сделано исключение, они располагаются в стеке. Если же нужен не примитивный тип int, а объект типа Integer (ну, к примеру, тебе понадобились специфические метода этого объекта), то надо объявить его так:
Integer x = new Integer(2);
В этом случае x является экземляром класса Integer и располагаться в куче.
На этом месте я закончу с выделением памяти, интересующиеся могут обратиться к книге Брюса Экеля «Философия JAVA» или к спецификации языка
III. Освобождение памяти. Алгоритмы сборщиков мусора
Надеюсь, все заметили, что в примере, приведенном выше, у класса FuckingExample нет деструктора. Это потому, что он не нужен. После того, как объект станет (на него не будет ссылки из статической переменной или активного фрейма стека), он будет передан сборщику мусора, который очистит от него память и вернет эту свободную память приложению.
Как же именно работают эти самые сборщики мусора? Ответ на это дает нам статья Брайана Гетца «Краткая история развития технологии утилизации памяти», которую я бессовестно спиздел, слегка переработав.
Выделяют два типа алгоритмов сборщика мусора: подсчет ссылок и трассирующие сборщики мусора.
Подсчет ссылок
Идея очень простая: Каждый объект имеет ассоциированное с ним количество ссылок — число активных ссылок на данный объект. Если количество ссылок равно нулю — это мусор (недоступный для пользовательской программы), и он может быть переработан. Но эта простая идея требует значительной поддержки от компилятора и даёт дополнительную избыточность для мутатора (термин для пользовательской программы с точки зрения сборщика мусора). Каждый раз, когда модифицируется ссылка на указатель, например, с помощью оператора присваивания, или когда ссылка выходит за границу, компилятор должен генерировать код для того, чтобы обновить количество ссылок на указываемый объект. Если количество ссылок на объект доходит до нуля, среда выполнения может вернуть блок информации немедленно (и уменьшить количество ссылок для всех блоков, на которые ссылается возвращаемый блок) или поместить его в очередь для отсроченной утилизации.
Собственно, это все, что нужно знать про этот алгоритм сборки мусора. Все равно он на практике сейчас не используется.
Трассирующие сборщики мусора
Трассирующий сборщик останавливает приложение (хоть это и не нужно на весь период сборки мусора) и начинают трассировать объекты, начиная с корневого набора, и проходят по всем ссылкам, пока все доступные объекты не будут проверены. Корни могут быть найдены в регистрах программ, в локальных (стековых) переменных, в стеке каждого из потоков и в статических переменных.
Маркирующе-зачищающие сборщики мусора
Наиболее простая форма трассирующего сборщика мусора, впервые предложенная создателем языка Lisp Джоном. Маккарти в 1960 г., — это маркирующе-зачищающий сборщик, в котором среда выполнения останавливается, и сборщик посещает каждый живой узел, начиная с корней, и маркирует каждый посещённый узел. Когда не существует больше не отслеженных ссылок, сборка мусора завершена, и затем динамическая память зачищается (то есть каждый объект динамической памяти проверяется), а любой непомеченный объект регенерируется как мусор и возвращается в список свободной памяти.
Маркировка-зачистка проста для реализации, может легко восстанавливать циклические структуры и не даёт дополнительной нагрузки на компилятор или мутатор как подсчёт ссылок. Но у неё есть недостатки — паузы на сбор мусора могут быть долгими, и вся динамическая память посещается в фазе зачистки, что может иметь весьма негативные последствия на работу систем с виртуальной памятью, где динамическая память может быть разбита на страницы.
Большая проблема с маркировкой-зачисткой состоит в том, что каждый активный (то есть, распределённый) объект, доступен он или нет, посещается в фазе зачистки. Потому что значительный процент объектов, вероятно, окажется мусором, что означает, что сборщик тратит значительные усилия на проверку и обработку мусора. Маркирующе-зачищающие сборщики мусора также имеют тенденцию фрагментировать динамическую память, что может вызвать проблемы с локализацией и может также стать причиной ошибок выделения памяти, даже когда доступно достаточное количество свободной памяти.
Копирующие сборщики мусора
В копирующем сборщике, другом виде маркирующего сборщика, динамическая память делится на две равные по размеру части, одна из которых содержит активные данные, а другая не используется. Когда активная часть заполнена, среда выполнения останавливается, и действующие объекты копируются из активной части в неактивную. При этом части меняются своими ролями: старая неактивная часть становится активной и наоборот.
Преимущество копирующих сборщиков мусора состоит в том, что они посещают только живые объекты, что означает, что объекты мусора не будут исследоваться, им не потребуется подкачка в память и помещение в кэш. Продолжительность циклов сборки мусора копирующего сборщика зависит от количества живых объектов. Однако, копирующие сборщики создают дополнительные затраты на копирование данных из одной части в другую с исправлением всех ссылок на местонахождение новой копии. В частности, долгоживущие объекты будут копироваться туда-сюда при каждой сборке мусора.
Копирующие сборщики приносят пользу ещё и тем, что живые объекты уплотняются в нижнюю часть динамической памяти. Это не только улучшает локальность ссылок пользовательской программы и позволяет избежать фрагментации динамической памяти, но и значительно уменьшает затраты на размещение объекта. Эта процедура становится просто добавлением ссылки к указателю вершины динамической памяти. Нет необходимости поддерживать списки свободной памяти или сохраняющиеся списки или выполнять алгоритмы наилучшей подгонки или первого подходящего — выделение места для N байт сводится к добавлению N к указателю вершины динамической памяти и возвращению его предыдущего значения.
Разработчики, которые использовали сложные схемы управления памятью для языков, не использующих сборщики мусора, могли бы сильно удивиться тому, насколько незатратным может быть распределение памяти — простое добавление ссылки — в копирующем сборщике мусора. Это может быть одной из причин распространённого мнения, что распределение объектов связанно с затратами, в более ранних версиях виртуальной машины Java не использовались копирующие сборщики мусора, и разработчики все еще подспудно считают, что затраты на распределение такие же, как в других языках, типа C, тогда как на самом деле они могут быть значительно меньше в исполняемой среде Java. Но не только издержки на распределение памяти меньше, но также для объектов, которые превратились в мусор до начала следующего цикла сборки, затраты на освобождение памяти равны нулю, так как утилизируемый объект не будет ни проверяться, ни копироваться.
Маркирующе-сжимающие сборщики мусора
Копирующий алгоритм имеет прекрасные рабочие характеристики, но его основным недостатком является потребность в объёме памяти большем в два раза, чем объём, необходимый для маркирующе-зачищающего сборщика мусора. Алгоритм маркировки-сжатия сочетает маркировку-зачистку и копирование таким образом, что эта проблема исчезает за счет того, что процесс сборки мусора усложняется. Как и маркировка-зачистка, маркировка-сжатие состоит из двух фаз. В фазе маркировки каждый действующий объект рассматривается и маркируется. Затем маркированные объекты копируются таким образом, что все действующие объекты уплотняются в самом низу динамической памяти. Если полное сжатие совершается при каждой сборке мусора, полученная в результате динамическая память такая же как и результат работы копирующего сборщика — между активной частью динамической памяти и свободной существует чёткая граница, так что затраты на размещение сравнимы с затратами при использовании копирующего сборщика мусора. Долгоживущие объекты имеют тенденцию накапливаться в самом низу динамической памяти, поэтому они не копируются циклически, как при использовании копирующего сборщика мусора.
Upgrade
15.06.2008
Приступаю к модернизации сайта.
В связи с отделочно-покрасочными работами, всю неделю сайт будет лихорадить. Выражаться это будет в чем угодно, вплоть до полной остановки сайта.
Особенно предупреждаю подписчиков по rss — алгоритм выдачи xml будет изменен, так что вполне возможны временные баги во внешнем виде параграфов, их содержании и порядке следования.
Планируемый список изменений:
- Новый дизайн сайта.
- Новая система комментирования и редактирования комментариев.
- Новый lightbox — благодаря этому, кстати, вес страниц уменьшится где-то на ~200 KB.
- Рабочие уведомления по e-mail.
Opera 9.50 vs. Mac OS X
13.06.2008
Пишу этот пост из только что установленной Opera 9.50. Мне очень хочется рассказать о своих впечатлениях о пользовании этим замечательным браузером.
Кратко — пиздец. Все фанаты оперы могут скроллить страницу вниз и писать злобные комментарии. Остальным я постараюсь объяснить, что же мне так не понравилось.
Шрифты
Ослепительный пиздец. Не оттого, что так круто, а от того, что страшно смотреть на такое убожество. Читать невозможно, глаза болят через пять минут пользования.
Я имел наглость надеяться, что от такой ебанутой типографики избавился, пересев с никсов на мак. Нихуя подобного, есть еще ребята из Норвегии, легко крушащие мои надежды.

- Нет сглаживания шрифтов.
- С разрядкой кириллических шрифтов какая-то хуйня. Ну эта поебень уже вообще ни в какие ворота не лезет. В России у оперы чуть ли не самые лучшие позиции, а они так облажались. Неужели же никто не проверил сборку перед тем, как ее выкладывать, а?
- Курсив и некоторые шрифты и вовсе не поддерживаются — просто квадратики отображаются. Несмотря на то, что опера, как и любая другая программа, имеет доступ к папке fonts.
Предвижу возражения:
«Ты ничего не понимаешь, – будет возмущаться фанат оперы, – все эти проблемы решаются за пять минут гугления, там в конфиге надо пару параметров поправить!».
Ребята, я и не сомневаюсь. Можно. Просто. Вот только нахуя это мне надо? Я, блядь, пользователь, выбирающий инструмент. Меня вообще не должно ебать, как он там настраивается. Понятно, что если бы опера была единственным игроком на поле, то тогда можно было бы мириться с такими недостатками. Но я счастливый пользователь firefox’a, у третьей версии которого таких недостатков нет.
Для сравнения тот же сайт, с тем же текстом в других браузерах:


Интерфейс

И опять я вынужден повториться. Пиздец. Запредельный, мучительный пиздец. По дефолту стоит какая-то мегабеспомощная хуйня. Блядь, ну перед сборкой-то хоть одна сука посмотрела, как это будет смотреться на маке, нет? Любому долбоебу понятно, что при одном взгляде на это убожество никто не полезет в настройки, не будет скачивать другие скины, а просто удалит оперу и останется на safari/firefox. Только блядь преданный фанат останется.
Какой нахуй Cocoa style? Какие-такие руководства по стилю? Бросьте, ребята из Opera Software ASA выше этого. На хую они вертели дизайнеров OS X, разработчиков OS X, и отдельным порядком — пользователей OS X. Им бы, блядь, хотя бы угнаться за конкурентами на win* платформах.
Аналогичные элементы интерфейса, для сравнения, в других браузерах:


Dragonfly
Картинка кликабельна
Отдельно стоит упомянуть dragonfly. Штука действительно хорошо работает. Все, что нужно front-end web-разработчику, в ней есть. Только мне решительно непонятно — отчего кто-то называет этот плагин убийцей firebug for firefox. Функционал абсолютно идентичный. Настолько идентичный, что у меня возникают смутные подозрения в том, что опера внаглую спиздела все фичи c firebug'a.
Все, с меня хватит. Если кому есть что сказать — добро пожаловать в комментарии. Я же возвращаюсь на firefox и буду их оттуда читать.
Астрология
03.06.2008
На одном ресурсе я встретил пост с названием «Для тех, у кого девушка — Овен». Переведите на русский: «Для тех, кто встречается с овцой». Блядь, хоть иногда-то надо включать мозги.
Меня бесит астрология и гороскопы. И еще люди, которые серьезно воспринимают других людей сквозь призму этой лженауки.
Почему-то особенно падки на этот обман девушки. Странно смотреть, как образованная, здравомыслящая в других отношениях девушка слепо верит написанному за столетия до ее рождения бреду, не подвергая прочитываемое никакому критическому анализу.
Ну вы вдумайтесь, как можно серьезно относиться к тому, что время рождения человека под какими-то звездами может оказать влияние на его характер, физическое состояние и судьбу?
Я не говорю о том, что в масштабах вселенной все люди родились в одно и то же время и под одними и теми же звездами — все же человечество слишком мало существует, чтобы солнечная система сдвинулась на достаточно большое расстояние. Оставим это, тем более что для многих это слишком сложно.
Я не говорю о том, что за то время, когда плод находится в животе у мамы, он десятки раз побывал под светом почти всех этих созвездий.
И не говорю о том, что человек всю жизнь проходит под светом всех этих созвездий — почему же предпочтение отдается только тому знаку, под которым он родился?
Я говорю вот о чем: возьми эту книжку, в которой перечислены все знаки задиака — гороскопник или как там она называется. Взяла? Открой на случайной странице, только чтобы там не было озаглавлено, что это за знак. Открыла? Читай. Прочитала? Ну, теперь можешь с полным правом восклицать:
«О, это про меня!»
«Да, узнаю себя в этом!»
«Точно-точно, так и есть!»
Теперь пролистай на страницу названия знака — думаю, ты будешь удивлена.
Все дело в том, что по гороскопу просто раскиданы общие фразы, составленные в обтекаемые предложения. И эти черты подходят любому человеку. Вообще любому. Любой человек в тот или иной момент своей жизни испытывает лень, или нетерпимость, или подъем настроения, или депрессию. Открыв любую страницу любого гороскопа, ты найдешь что-то для себя.
Знаешь, от чего это так? Нет, не оттого, что это все какой-то заговор. Это оттого, что в средние века, или когда там составлялись описания знаков, умным людям хотелось жить в тепле и достатке, а также иметь средства на занятие действительно интересными делами, в том числе и настоящей наукой. И вот эти умные люди, придворные маги и алхимики, и составили вот эти описания — для того, чтобы разводить дремучих дворян и мещан, которые велись на красиво составленные фразы.
Также работают гадалки:
— О, вижу! Тебя ждет большая любовь, только опасайся своей черноволосой подруги!
То, что заметить по внешнему виду девушки, что она одинока (а значит, ищет любви) достаточно легко, и то, что у любого человека есть как минимум одна черноволосая приятельница — на это и расчет. Человек заглотнул наживку, и теперь, когда он уже поверил гадалке, можно впаривать ему любую чушь.
А вот опасность в гороскопах действительно есть — когда ты читаешь описание „своего“ знака, веришь в него и соглашаешься с недостатками/ограничениями, указанными для знака, ты невольно начинаешь себя вести в соответствии с тем, во что веришь и таким образом сама себя ограничиваешь. Напоминает эффект плацебо.
Короче. Все эти гороскопы — глобальное наебательство. Цель у него, как и у всех наебательств — стянуть деньги. Ты не поверишь — столетиями не меняются способы, с помощью которых с доверчивых лохов тянут деньги.
Не будьте лохами.
Не ведитесь на средневековые разводы.
Не засоряйте себе мозги чушью.
Побольше думайте.
И помните — ваш характер, ваше физическое состояние, настроение, судьба — только в ваших руках. Никто и ничто не может никак на вас повлиять без вашего позволения.
Russify Comments Number
30.05.2008
Прошу прощения за долгий перерыв между постами, заела рутина.
Пост про инструменты тестирования и настройку ПО под макось пишется, и ожидается в начале следующей недели.
Пока же, чтобы облегчить ожидание, вот немножко сладкого (только для движка wordpress, к сожалению):
Во время настройки блога меня настолько сильно достало неправильное склонение слова «комментарий» в выводе числа комментариев к посту, что я занялся этой проблемой и написал супер-простой плагин под wordpress, решающий эту проблему.
Думаю, пригодится владельцам standalone блогов на движке wordpress.
RSS


