12 окт. 2010 г.

Отладка Java приложений

Теоретически, каждый программер еще на этапе разработки должен следить куда у него уходит оперативка. В некоторых IDE, например NetBeans, даже есть штатный профайлер, через который можно следить за состоянием оперативки... Но как показывает практика - все это теория. В "песочнице" на локальной машине адекватную нагрузку не создашь, все глюки обычно вылазят когда проект уже заливается на серваки...
В яве есть встроенная система отладки. Собсно рассказ будет коротко о ней.
JConsole - средство для мониторинга.

Кратко по вкладкам:
  • Overview - общие графики (память, потоки, классы, проц)
  • Memory - память
  • Threads - потоки
  • Classes - загруженные классы в JVM
  • VM Summary - общая информация о JVM
  • MBeans - расширения для JMX (есть базовые, можно писать свои)

После запуска JConsole - будет окошко, где можно выбрать процесс на локальной машине, или ввести адрес удаленной машины, где запущено приложение с JMX.

Для того что бы подключиться к процессу на локальной машине - достаточно просто выбрать его в блоке "Local Processes". Собственно тут сложности не должно быть.
То что больше нас интересует - это как подключится к удаленному процессу. Для начала зайдем на удаленную машину, найдем скрипт запуска той программы которая нас интересует. Я буду показывать на примере Tomcat.
Для начала откроем catalina.sh и найдем блок где у нас запускается процесс:
"$_RUNJAVA" "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMPDIR" \
org.apache.catalina.startup.Bootstrap "$@" start \
>> "$CATALINA_BASE"/logs/catalina.out 2>&1 &
нам нужно добавить:
-Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false \
где:
  • -Dcom.sun.management.jmxremote.port=8004 - порт который будет слушать JMX
  • -Dcom.sun.management.jmxremote.authenticate=false - без авторизации
  • -Dcom.sun.management.jmxremote.ssl=false - без SSL
в итоге получим что-то вроде такого:
"$_RUNJAVA" "$LOGGING_CONFIG" $JAVA_OPTS $CATALINA_OPTS \
-Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \
-Dcatalina.base="$CATALINA_BASE" \
-Dcatalina.home="$CATALINA_HOME" \
-Djava.io.tmpdir="$CATALINA_TMPDIR" \
-Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false \
org.apache.catalina.startup.Bootstrap "$@" start \
>> "$CATALINA_BASE"/logs/catalina.out 2>&1 &
Теперь после перезапуска томката в списке процессов мы должны увидеть наш томкат вместе с новыми "добавлениями":
dip56245@system2:~> ps aux | grep -i java
dip56245 13228  0.0  1.5 423908 48536 ?        Sl   Oct08   0:17 /opt/jdk/bin/java -Djava.util.logging.config.file=/opt/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/opt/tomcat/endorsed -classpath :/opt/tomcat/bin/bootstrap.jar -Dcatalina.base=/opt/tomcat -Dcatalina.home=/opt/tomcat -Djava.io.tmpdir=/opt/tomcat/temp -Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false org.apache.catalina.startup.Bootstrap start
Пробуем подключаться на своей машине на тот порт который мы указали, либо просто запустим jconsole и введя данные в окне, либо прям в командной строке написав:
$jconsole [ip]:[port]
Если все сделано правильно - то мы увидем графики. Если нет - проверяем файрволл, проверяем логи на серваке. Из найденных боков - т.к. это все работает на RMI, то нужно что бы на удаленной машине правильно резолвился IP и имя на внешний IP адрес (по которому мы собрались подключаться).
Если у Вас все получилось - и в JConsole ползут графики, уже можно хоть немного проанализировать ситуацию. Но оставлять в таком виде (без авторизации) очень не хорошо. Поэтому добавляем авторизацию на нашу систему, на удаленной системе в в catalina.sh меняем:
-Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false \
на
-Dcom.sun.management.jmxremote.port=8004 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.password.file=/opt/tomcat/conf/jmxremote.password \
-Dcom.sun.management.jmxremote.access.file=/opt/tomcat/conf/jmxremote.access \
мы добавили 2 параметра, путь к файлу паролей (jmxremote.password):
user1 passworduser1
user2 passworduser2
и файл прав доступа (jmxremote.access)
user1 readwrite
user2 readonly
получается у нас будет пользователь user1 с паролем passworduser1 который имеет право производить какие-либо изменения и соответственно user2 у которого права только на просмотр.
Перезапускаем томкат и пробуем подключиться в jconsole. Если все сделано правильно - то мы без пароля уже зайти на наш сервер не сможем. Можно продолжать дальше.
Мы можем теперь просмотреть нагрузку, расход оперативки и т.д. Но, предположим что мы видим постоянный расход оперативки, т.е. в простонародйе "memory leak". Как быть?
Есть несколько способов, один из них:
  • заходим в jconsole во вкладку MBeans
  • слева выбираем com.sun.management
  • в подпункте выбираем HotSpotDiagnostic
  • в подпуктах HotSpotDiagnostic выбираем Operations
  • в основной вкладке (справа) находим dumpHeap и вместо String пишем путь куда нам нужно сохранить дамп памяти (путь на удаленной машине)
  • нажимаем кнопку dumpHeap

теперь на удаленной машине по указанному нами пути будет создан файл с дампом памяти. Его нужно просмотреть. Есть несколько способов, есть сторонние анализаторы и т.д., но самый простой - это на удаленной машине пишем:
$jhat %path_to_dump%
в консоли появиться нечто вроде:
dip56245@dip56245:~$ jhat /tmp/mydump 
Reading from /tmp/mydump...
Dump file created Tue Oct 12 11:49:48 EEST 2010
Snapshot read, resolving...
Resolving 169981 objects...
Chasing references, expect 33 dots.................................
Eliminating duplicate references.................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
по умолчанию (если ключами не менять) у нас создастся WebServer на 7000 порту, и теперь можно зайти обычным браузером и посмотреть отчет по нашей оперативке.
Собственно все.

Комментариев нет: