Это сообщение от Владимира Шора из блога Plumbr.
Наша компания занимается тем, чтобы сделать причину ошибок программного обеспечения прозрачной для разработчиков и операций. В отличие от альтернативных решений, мы выявляем местоположение проблемы, указывая вам на вредоносную строку в исходном коде. Несмотря на то, что в настоящее время мы наиболее известны своими возможностями по обнаружению утечек памяти, мы расширяемся и в других областях. Чтобы дать вам подсказку о наших направлениях исследований, мы решили поделиться этим на трех примерах.
Примеры сводятся к возможностям JVM для создания значимых стековых трасс. Во многих случаях трассировка стека содержит всю информацию, необходимую для решения проблемы. В других ситуациях это только поверхностные симптомы, без понятия о том, что может быть причиной проблемы. Позвольте мне проиллюстрировать это тремя примерами, вызывающими следующие печально известные сообщения об ошибках:
- java.lang.OutOfMemoryError: невозможно создать новый собственный поток
- java.io.IOException: слишком много открытых файлов в системе
- java.lang.OutOfMemoryError: пространство кучи Java
Все примеры проиллюстрированы простыми фрагментами кода, облегчающими понимание основной проблемы.
Слишком много тем
static void createTooManyThreads() { try { for (int i = 0; i < TOO_MANY; i++) { Thread t = new Thread(new Runnable() { public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } } catch (OutOfMemoryError e) { e.printStackTrace(); } }
В приведенном выше коде мы продолжаем запускать потоки, пока не достигнем системного предела и не встретим сообщение «java.lang.OutOfMemoryError: не в состоянии создать новый собственный поток» . Что плохого в понимании того, что проблема связана с исчерпанием ограничения потока? Давайте ближе рассмотрим трассировку стека:
java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:693) at eu.plumbr.demo.MisleadingStacktrace.createTooManyThreads(MisleadingStacktrace.java:34) at eu.plumbr.demo.MisleadingStacktrace.main(MisleadingStacktrace.java:16)
Проблема смотрит прямо в наше лицо — нам говорят, где спина верблюда была сломана, добавляя последнюю соломинку. В то же время у нас нет понятия, чья вина в том, что верблюд уже загружен в полной мере. Если бы сообщение об ошибке содержало также способ увидеть различные следы, относительно которых стеки вызовов потребляли потоки до того, как наша последняя попытка запустить последнюю привела к описанной выше трассировке стека, это сделало бы жизнь разработчиков намного проще.
Но давайте посмотрим на ту же проблему — потребление ресурсов с другой точки зрения:
Слишком много открытых файлов
Опять же, давайте начнем с примера кода:
static void createTooManyFiles() { try { for (int i = 0; i < TOO_MANY; i++) { File f = new File(PATH_TO_FILE + i + ".txt"); f.createNewFile(); OutputStream s = new FileOutputStream(f); s.write(1); } } catch (IOException e) { e.printStackTrace(); } }
Образец пытается создать много файлов и записать только одно целое число в каждый из файлов, не закрывая предыдущие. И снова, выполнение приведенного выше кода приводит к не слишком полезной трассировке стека:
java.io.IOException: Too many open files in system at java.io.UnixFileSystem.createFileExclusively(Native Method) at java.io.File.createNewFile(File.java:947) at eu.plumbr.demo.MisleadingStacktrace.createTooManyFiles(MisleadingStacktrace.java:45) at eu.plumbr.demo.MisleadingStacktrace.main(MisleadingStacktrace.java:17)
Та же самая проблема теперь замаскирована по-другому — мы получаем сообщение о том, что я сейчас слишком много пытался открыть один файл, но — кто открыл другие файлы, чтобы подчеркнуть JVM до такой степени, что он не может завершить выполнение?
Если вы все еще не уверены — взгляните на третий пример нашего нынешнего хлеба с маслом:
Слишком много памяти занято
Пример кода снова прост — мы берем структуру данных и продолжаем увеличивать ее, пока доступная куча не будет исчерпана:
static void createTooLargeDataStructure() { try { List l = new ArrayList(); for (int i = 0; i < TOO_MANY; i++) { l.add(i); } } catch (OutOfMemoryError e) { e.printStackTrace(); } }
Запуск кода дает вам печально известное сообщение java.lang.OutOfMemoryError: пространство кучи Java . Что, опять же, трудно интерпретировать, если рассматриваемая структура данных была заполнена в различных возможных местах исходного кода.
Я знаю, что все коллеги-разработчики теперь беспомощно пожимают плечами, потому что в их мире все вышеперечисленное просто приняло бы форму ошибки, но — если бы мы зашли так далеко, почему бы нам не добиться большего? Я уверен, что мы можем, и вот что мы собираемся сделать — найти узкие места производительности для вас.
Если вас интересует полный пример кода, его можно скачать здесь . Код запускался на моем Macbook Pro с OS X 10.9 на JDK 7u25. После этого убедитесь, что вы не пропустите будущие обновления интересного контента, и подпишитесь на нашу ленту Twitter .