e5c9fe3900fbea92347fcd88e487dd55.ppt
- Количество слайдов: 78
Вопросы производительности Java приложений Андрей Дмитриев Инженер-программист Sun Microsystems Февраль 2008
Что такое производительность? • • • Вычислительная сложность. Использование памяти. Скорость запуска приложения. Запас прочности и расширяемость. Видимая скорость. 2
Вычислительная сложность • Количество операций в единицу времени. • Накладные расходы на вызов методов. • Сложность алгоритма для данного класса задач. 3
Использование памяти • Количество памяти, требуемое для обычной работы программы, должно быть минимизировано. • Увеличение нагрузки на систему не должно выражаться в существенном росте потребления памяти. • Не должно быть «потерянной памяти» (memory leak). 4
Скорость запуска • Время, необходимое для запуска приложения может быть критично. • Виртуальная машина может оптимизировать некоторые методы для скорейшего их исполнения. • Программа может не обладать всей функциональностью в начале запуска. 5
Расширяемость • Простота поддержки системы. • «Предсказуемость» поведения приложения при повышении нагрузок. 6
Видимая скорость • Приложение может обладать некими характеристиками, благоприятно влияющими на общее впечатление от программы. • Действительная производительность может не соответствовать идеальной картине. 7
Программа • Введение. – Цикл проекта по оптимизации. – Инструментарий. • Вопросы программирования. – Ввод-вывод. – Использование памяти. – Сложность алгоритма. • Использование системных возможностей. – Настройки сборщика мусора. – Другие возможности виртуальной машины. 8
Процесс по оптимизации • • Анализ. Дизайн. Реализация. Тестирование. 9
Анализ • Является первым шагом при > При проектировании > При оптимизации • Требует наличия некоторых требований, от которых можно оттолкнуться. > > Минимум памяти. Минимальная скорость CPU. Минимальная скорость соединения. И др. • Profiling – поиск областей, где тратится наибольшее количество ресурсов. > При профилировании происходит сбор информации о показателях программы в тех или иных ситуациях. > Профилирование часто связано с анализом полученных 10 данных.
Дизайн • Создание общей схемы системы с различной степенью детализации. • Могут быть использованы различные методологии. • При оптимизации это оценка, теоретическое доказательство предполагаемого подхода. 11
Реализация • Стиль написания или изменения программы имеет ключевое влияние на качество программы. • При оптимизации накладываются качественные рамки в виде тестов на производительность на код. 12
Тестирование • На этапе оптимизации предполагается, что качество достигло необходимого уровня. • Построение базы тестов должно начинаться на самых ранних этапах создания или оптимизации системы. • Benchmarking – сравнение показателей двух различных случаев. • Тестирование производительности важнее, чем тестирование качества. 13
Как оценивать производительность? • Запуск приложения. • Открытие большого документа. • Длительность выполнения запроса. • И т. п. 14
Измерение временных интервалов • Инструментирование кода. • Программные вызовы (System. current. Time. Millis()). • Системные возможности. • Эталонные тесты. • Micro benchmark – многократное выполнение конкретного действия. • Macro benchmark - многократное выполнение конкретного действия в совокупности с типичным набором сопутствующих операций. • Сюиты могут выравнивать дисперсию результатов, увеличивая число попыток. 15
Узкие места программы (hot spot, bottleneck) • Это методы или блоки кода, которые • выполняются дольше всего, • вызываются наиболее часто, • требуют больше всего памяти. • Стратегии. • Реализовать эффективнее, сделать быстрее часто исполняемые методы. • Минимизировать число вызовов медленных методов. 16
Состояние среды исполнения Возможности VM позволяют: • составлять слепки области памяти в любой момент времени; • считывать данные о загрузке процессора. 17
Структура области памяти Итоги размещения данных в памяти: > java -Xrunhprof Application Dumping Java heap. . . allocation sites. . . done. percent rank live self alloc'ed stack class accum bytes objs bytes 1 13. 83% 221928 3891 221928 objs trace name 3891 300000 char[] 2 5. 85% 19. 68% 93920 3899 93920 3 2. 19% 21. 87% 35112 5 305309 byte[] 4 1. 60% 23. 47% 25600 10 300000 byte[] 5 1. 18% 24. 64% 18856 5 305353 byte[] 6 1. 02% 25. 67% 16400 1 305831 int[] 7 0. 79% 26. 46% 12672 8 55240 32 300045 byte[] 8 0. 74% 27. 20% 11904 2 305389 int[] 9 0. 64% 27. 84% 10248 427 20544 … 3899 300000 java. lang. String 856 302753 18
Структура области памяти Стек вызовов методов в приложении: TRACE 304224: java. lang. String.
Загрузка процессора java -Xprof Application Interpreted + native Method 18. 5% 0 + 10 sun. awt. Win 32 Graphics. Environment. init. Display 3. 7% 2 + 0 sun. nio. cs. ISO_8859_1$Decoder. decode. Array. Loop 1. 9% 0 + 1 sun. awt. windows. WData. Transferer. register. Clipboard 1. 9% 0 + 1 sun. awt. windows. WComponent. Peer. p. Show 1. 9% 0 + 1 java. lang. Class. Loader$Native. Library. load 1. 9% 0 + 1 java. lang. Class. Loader$Native. Library. find 1. 9% 1 + 0 sun. awt. windows. WToolkit. get. Screen. Insets 1. 9% 0 + 1 java. lang. Throwable. fill. In. Stack. Trace 1. 9% 0 + 1 sun. awt. windows. WFrame. Peer. set. State 1. 9% 0 + 1 java. io. Win. NTFile. System. canonicalize. With. Prefix 0 … 20
Дополнительный инструментарий • jmap – утилита для сохранения данных о структуре данных в куче. • jhat – анализатор отчетов утилиты jmap. • JConsole – удаленное профилирование приложений. 21
Выводы • Под «производительностью» можно подразумевать различные физические параметры. • Приложение может снабжаться своим тестовым набором. • JDK поставляется с базовым профилирующим инструментарием. • Среда разработки или специализированные приложения могут предоставлять богатые возможности для отладки и профилирования. 22
Ввод/вывод • Пакет java. io предоставляет широкий спектр классов для работы с потоками данных. • Различные классы показывают лучшую производительность для разных задач. 23
Буферизация данных • Скорость чтения и записи на диск уменьшается при уменьшении блока данных. • Создание промежуточного буфера может принести существенное улучшение производительности. • Буферизация может осуществляться и средствами самой программы. 24
Взаимодействие с файловой системой • Виртуальная машина сама инициирует множество обращений к файловой системе. • Изменение поведения программы может повлиять на поведение виртуальной машины. • Существуют утилиты (filemon), отслеживающие все обращения указанного процесса к файловой системе. 25
Использование памяти • Использованная память (RAM footprint) программы включает в себя классы, объекты. • Класс java. lang. Runtime предоставляет функциональность для определения свободной и занятой памяти в куче. • Для оценки объема памяти, занятой приложением в целом, можно использовать средства платформы. 26
Из чего состоит занятая память? В памяти VM (heap) можно выделить данные нескольких типов: • Классы • Объекты • Потоки • Системные структуры данных • Системный код и библиотеки 27
Размер объекта • Определяется суммой размеров всех полей класса (включая super). • Виртуальная машина может выравнивать блоки. class Data. Storage{ int index; //4 байта byte value; //1 байт float rgb. Model; //4 байта double cmy. Model; //8 байт long native. Data; //8 байт //super -ссылка (обычно 4 байта) } 28
Размер класса Класс содержит: • байткод, • структуру методов и полей класса (необходима VM), • данные из пула констант, • предкомпилированный код (Just-In-Time, JIT). 29
Загрузка классов • Виртуальная машина загружает классы по мере необходимости. • Подсчет количества классов позволяет оценить суммарный объем. • Особенно важен при работе по сети. 30
Список загруженных классов Команда java –verbose Application составляет список всех классов, загруженных в память в данном сеансе работы. [Loaded sun. swing. Cached. Painter from C: jdk 1. 6. 0jrelibrt. jar] [Loaded javax. Painter from C: jdk 1. 6. 0jrelibrt. jar] [Loaded sun. swing. Cached. Painter$Cache from C: jdk 1. 6. 0jrelibrt. jar] [Loaded sun. swing. Cached. Painter$Entry from C: jdk 1. 6. 0jrelibrt. jar] [Loaded java. awt. Gradient. Paint from C: jdk 1. 6. 0jrelibrt. jar] [Loaded java. awt. Rendering. Hints from C: jdk 1. 6. 0jrelibrt. jar] [Loaded sun. awt. Sun. Hints from C: jdk 1. 6. 0jrelibrt. jar] … 31
Контроль загрузки классов • Загрузка большого количества классов может существенно увеличить объем используемой памяти. • Загрузка класса также требует от VM выполнения поисковой операции. • Класс загружается при: > Создании экземпляра класса, > Обращении к статическому полю или методу, > Использовании в instanceof. • Работа с объектами из С/С++ кода подчиняется тем же правилам. 32
Утечка памяти • • Обзор Garbage Collector. Причины возникновения утечек. Способы отслеживания. Методы предотвращения утечек. 33
Сборщик мусора • Технология автоматической сборки объектов, на которые отсутствуют жесткие ссылки позволяет не заботиться об освобождении памяти. • У программиста нет надежных способов воздействовать на сборщик мусора. 34
Как теряется память? • Несмотря на надежность работы GC, количество используемой программой памяти может увеличиваться. • Программа может не хранить прямые ссылки на объект, но содержать объект в составе коллекции. • При использовании JNI, ресурсы платформы не отслеживаются сборщиком мусора. 35
Отслеживание утечек • Поиск класса, количество экземпляров которого растет. • Системный ресурс: отслеживание с помощью диспетчера задач. • Java ресурс: отслеживание с помощью профилировщика. • Выделение областей в программе, где данные экземпляры создаются. • Допустимо контролировать размер всех коллекций. 36
Предотвращение утечек • Реализация. • Применение метода коллекции remove() для ненужных более объектов. • Организация проекта. • Создание сюиты нагрузочных тестов для приложения. • Модульное тестирование. 37
Насколько сильно потоки замедлят мое приложение? • Поток занимает место как объект и требует дополнительной памяти для хранения стека и, возможно, системных ресурсов. • Способ реализации потоков заложен в виртуальную машину. • Использование потоков может существенно улучшить скорость работы приложения. 38
Оптимизация загрузки классов • Задача. • Приложение должно обрабатывать различные типы документов. • Каждому документу назначается класс Translator. • Приложение может реализовать паттерн фабрика, возвращая конкретную реализацию класса Translator. 39
Неудачное решение При компиляции метода (JIT) загружаются все используемые классы. public static Translator get. Translator(String file. Type) { if (file. Type. equals("doc")) { return new Word. Translator(); } else if (file. Type. equals("html")) { return new HTMLTranslator(); } else if (file. Type. equals("txt")) { return new Plain. Translator(); } else if (file. Type. equals("xml")) { return new XMLTranslator(); } else { return new Default. Translator(); } } 40
Экономичное решение • Отложим действия по разрешению нужного класса до этапа выполнения. • Для этого используем механизм reflection. try { if (file. Type. equals("doc")) { return (Translator)Class. for. Name( "Word. Translator"). new. Instance(); } else if (file. Type. equals("html")) { return (Translator)Class. for. Name("HTMLTranslator"). new. Instance(); } else if (file. Type. equals("txt")) { return (Translator)Class. for. Name("Plain. Translator"). new. Instance(); } else if (file. Type. equals("xml")) { return (Translator)Class. for. Name("XMLTranslator"). new. Instance(); } else { return new Default. Translator(); } } catch (Exception e) { return new Default. Translator(); } 41
Оценка количества классов Программа обрабатывает сообщения от трех кнопок: public class Listener 1 extends JFrame { public Listener 1() { JButton open = new JButton("Open"); JButton close = new JButton("Close"); JButton save = new JButton("Save"); get. Content. Pane(). set. Layout(new Flow. Layout()); get. Content. Pane(). add(open); get. Content. Pane(). add(close); get. Content. Pane(). add(save); open. add. Action. Listener(new Open. Action()); //для каждого компонента нужен класс close. add. Action. Listener(new Close. Action()); //если компонентов много, то save. add. Action. Listener(new Save. Action()); //загружается чересчур много классов set. Visible(true); } 42
Оценка количества классов (cont. ) Каждый слушатель – это внутренний класс. protected void open() { //Open a file } protected void close() { //Close a file } protected void save() { //Save a file } class Open. Action implements Action. Listener { public void action. Performed(Action. Event e) { open(); } } class Close. Action implements Action. Listener { public void action. Performed(Action. Event e) { close(); } } class Save. Action implements Action. Listener { public void action. Performed(Action. Event e) { save(); } } public static void main(String[] args) { new Listener 1(); } } 43
Совмещение слушателей • Внедрение логики в один из классов-слушателей позволяет переложить на него выбор действия. class Button. Action implements Action. Listener { public void action. Performed(Action. Event e) { String action = (JButton)e. get. Source(). get. Text(); if ( action. equals("Open") ) { open(); } else if (action. equals("Close")) { close(); } else if (action. equals("Save")) { save(); Какие сложности привносит данный код в программу? 44
Использование общего слушателя • Теперь достаточно одного класса для всех компонентов интерфейса. Action. Listener listener = new Button. Action(); open. add. Action. Listener(listener); close. add. Action. Listener(listener); save. add. Action. Listener(listener); 45
Недостатки данного решения • Плохая структурированность программы: блок switch. • Зависимость от имен компонентов. • Сложность поддержки. 46
Использование отражения Также может уменьшить количество классов. class Reflective. Action implements Action. Listener { String method. Name; Object target; public Reflective. Action(Object target, String method. Name) { this. target = target; this. method. Name = method. Name; } … 47
Использование отражения (cont. ) Данный метод осуществляет вызов установленного на этапе создания объекта обработчика для сообщений. … public void action. Performed(Action. Event e) { try { Class[] arg. Types = {}; Method method = target. Class(). get. Method(method. Name, arg. Types); Object[] args = {}; method. invoke(target, args); } catch (Exception ex) { ex. print. Stack. Trace(); }}} 48
Использование отражения (cont. ) Потребуется создавать несколько экземпляров для различных компонентов. open. add. Action. Listener(new Reflective. Action(this, "open")); close. add. Action. Listener(new Reflective. Action(this, "close")); save. add. Action. Listener(new Reflective. Action(this, "save")); Можно объединять одинаковые компоненты одним таким классом. Код не сильно усложняется. 49
Дальнейшее улучшение • Применить паттерн Заместитель (proxy) для того, чтобы переиспользовать объекты в течение жизни программы. • Этим можно достичь константного количества экземпляров классов во время исполнения программы. 50
Пример: запуск приложений в одной JVM • Каждое Java приложение требует для своей работы JVM. • Второе приложение, запущенное стандартным способом, не имеет возможности выполняться внутри уже существующей JVM. • Приложения, работающие в разных JVM, не имея возможности их разделять, дублируют все классы. 51
Структура сложного приложения • Существует офисный пакет из нескольких приложений. Электронная таблица: public class Spreadsheet { public static void main(String[] args) { JFrame f = new JFrame("Spreadsheet"); JTable table = new JTable(20, 5); JScroll. Pane scroller = new JScroll. Pane(table); f. set. Content. Pane(scroller); f. set. Bounds(10, 200, 200); f. set. Visible(true); f. add. Window. Listener(new Window. Adapter() { public void window. Closing(Window. Event e) { System. exit(0); }}); } } 52
Структура сложного приложения (cont. ) Текстовый процессор. public class Word. Processor { public static void main(String[] args) { JFrame f = new JFrame("Word. Processor"); JText. Area text = new JText. Area("Type Here"); JScroll. Pane scroller = new JScroll. Pane(text); f. set. Content. Pane(scroller); f. set. Bounds(10, 200, 200); f. set. Visible(true); f. add. Window. Listener(new Window. Adapter() { public void window. Closing(Window. Event e) { System. exit(0); }}); } } 53
Показатели запуска приложений • > java Spreadsheet • > java Wordprocessor 54
Реализация запуска в одной JVM При попытке запустить приложение: • Загрузчик определяет, есть ли уже готовая JVM? • Если есть, то запустить приложение в ней и выйти. • Если нет, то запустить новую JVM и остаться в памяти слушать последующие обращения. 55
Класс Launcher • В первый раз запускает JVM с сервисом. • Для взаимодействия использует класс Socket. public class Launcher { static final int socket. Port = 9876; public static void main(String[] args) { Launcher l = new Launcher(); l. launch(args[0]); } … 56
Класс Launcher (cont. ) • Попытка найти другие JVM с включенным сервисом: public void launch(String class. Name) { boolean launched = false; while (!launched) { Socket s = find. Service(); if (s != null) { System. out. println("found service"); … 57
Класс Launcher (cont. ) • Передача сервису имени класса приложения, который нужно запустить: Output. Stream o. Stream = s. get. Output. Stream(); byte[] bytes = class. Name. get. Bytes(); o. Stream. write(bytes. length); o. Stream. write(bytes); o. Stream. close(); launched = true; System. out. println(class. Name); 58
Класс Launcher (cont. ) • Если сервиса еще нет, то создаем его: System. out. println("Starting new service"); Server. Socket server = new Server. Socket(socket. Port); Launcher. go(class. Name); Thread listener = new Listener. Thread(server); listener. start(); launched = true; System. out. println("started service listener"); 59
Сервисный класс • Класс постоянно находится в ожидании подключения по сети. public class Listener. Thread extends Thread { Server. Socket server; public Listener. Thread(Server. Socket socket) { this. server = socket; } public void run() { while (true) { … String class. Name = new String(bytes); Launcher. go(class. Name); } } 60
Запуск работающей JVM • Приложение запускается посредством обращения к его методу main() через механизм отражения. class Launcher… public static void go(final String class. Name) { Thread thread = new Thread() { public void run() { Class clazz = Class. for. Name(class. Name); Method method = clazz. get. Method("main", {String[]. class}); method. invoke(clazz, {new String[0]}); } thread. start(); } 61
Результат работы загрузчика Приложения разделяют ресурсы между собой. 62
Изменяемость объекта • Объект может обладать свойством сохранять свое состояние без изменения. • По статистике, такие объекты существуют недолго. • Наоборот, изменяемый объект предназначен для длительной работы. • VM осуществляет мощных оптимизации над неизменяемыми объектами. > Строка – неизменяемый объект. > Все методы по модификации строки создают новые объекты. 63
Неизменяемое значение или ссылка? Модификатор final предотвращает изменение: public class Fruits { public static final String[] names = {“apple", “banana"}; } Fruits. names = new String[50]; //запрещено Fruits. names[1] = “grape”; //разрешено 64
Кэширование объектов • - это сохранение существующих объектов для дальнейшего возможного их использования. • Целесообразность такого шага должна быть оценена на основе числовых оценок. 65
Алгоритмы и структуры данных • Выбор алгоритма обработки данных может существенно повлиять на производительность. • Принцип организации данных в коллекциях объектов важен для выбора подходящей реализации. 66
Два алгоритма подсчета суммы прогрессии public class Simple. Summer { public long sum(int start, int stop){ long acc = 0; for(int i=start; i<=stop; i++) acc += i; return acc; } } public class Formulaic. Summer { public long sum(int start, int stop){ int bigseries = stop*(stop+1)/2; start--; int littleseries = start*(start+1)/2; return bigseries-littleseries; } } 67
Рекурсия • Вызов функцией самой себя естественным образом накладывается на некоторые прикладные задачи. • Эффективность рекурсии должна быть оценена с позиции: • использования памяти (метод может использовать промежуточные данные), • глубины (стек вызовов требует больше памяти). 68
Выбор структуры данных • Набор коллекций (пакет java. util) Java состоит из: > Списков (List), > Множеств (Set), > Ассоциативных массивов (Map). • Каждый тип содержит несколько различных реализаций. > Применимость того или иного конкретного класса оценивается по роду задачи. > Поиск > Модификация > Выборка. 69
Синхронизация коллекций • Позволяет исключить параллельный доступ к данным коллекции: import java. util. Collections; List list = Collections. synchronized. List(new Array. List()); • Этим привносятся дополнительные накладные расходы на работу с данными. 70
Неизменяемые коллекции • Предотвращает изменение данных коллекции. import java. util. Collections; List list = Collections. unmodifiable. List(new Array. List()); • Исключаются действия по копированию данных, необходимые поддержания содержимого в необходимом порядке. 71
Выводы • Производительность алгоритма зависит от области его применения. • Коллекции Java предоставляют широкий набор структур, алгоритмов и возможностей для работы с данными. 72
Системнозависимый код Использование JNI в Java приложениях может быть оправдано: • Нужно наладить взаимодействие с существующим С кодом. • При реализации JVM. • Стоит задача внедрения JVM в другую программу. • Вызов функций, специфичных для операционной системы. • Доступ к специальным аппаратным устройствам. • Создание критичных по времени выполнения частей кода. 73
Системнозависимый код (cont. ) • Вызов Java-Java может быть быстрее Java-C (и С-Java) вызовов в несколько раз. • Скорость обработки JNI вызовов находится в зависимости от реализации JVM. 74
Сборка мусора • • Почему нужно иметь в виду данный фактор? Гарантии, даваемые GC. Жизненный цикл объекта. Ссылки на объекты. 75
Ссылки по теме • • Effective Java, J. Bloch Java Platform performance, S. Wilson, J. Kesselman Java Performance Tuning, Jack Shirazi Ken Arnold, James Gosling, “The Java programming language”. • Richard Jones, “Garbage collection: algorithms for automatic dynamic memory management”. • Liang Sheng, “The Java Native Interface”. • Platform Computing. Multithreaded and Networked Programming, Thomas W. Christopher and George K. Thiruvathukal. 76
Q&A
Вопросы производительности Java приложений Андрей Дмитриев Andrei. Dmitriev@Sun. COM Инженер-программист Sun Microsystems Февраль 2008


