После работы все больше и больше кода было написано, программа становится все более и более раздутой, и эффективность становится все меньше и меньше. Это абсолютно не допускается для такого программиста, как я, который преследует совершенство. Следовательно, в дополнение к постоянно оптимизации структуры программы, оптимизация памяти и настройка производительности стала моими обычными «трюками».
Чтобы оптимизировать и настроить Java -программы памяти и производительность, определенно невозможно не понимать внутренних принципов виртуальных машин (или более строгих спецификаций). Вот хорошая книга «Глубокая виртуальная машина Java (второе издание)» (Билл Веннерс, перевод Cao Xiaogang и Jiang Jing. На самом деле эта статья является личным пониманием автора виртуальных машин Java после прочтения этой книги). Конечно, преимущества понимания виртуальных машин Java не ограничиваются двумя вышеуказанными преимуществами. С более глубокой технической точки зрения понимание спецификаций и реализации виртуальных машин Java будет более полезно для нас писать эффективный и стабильный код Java. Например, если мы понимаем модель памяти виртуальной машины Java и механизм утилизации памяти виртуальной машины, мы не будем слишком сильно полагаться на нее, но явно «выпустить память», когда это необходимо (код Java не может явно выпустить память, но мы можем сообщить коллекционеру мусора, что объект необходимо зацикливаться на снятии ссылки на объект), чтобы уменьшить уместную память; Если мы понимаем, как работает стек Java, мы можем снизить риск переполнения стека, уменьшив количество рекурсивных слоев и количество петель. Для разработчиков приложений они не могут напрямую включать работу базовой реализации этих виртуальных машин Java, но понимание этого фонового знания будет более или менее оказывать тонкое и хорошее влияние на программы, которые мы пишем.
Эта статья кратко объяснит модель архитектуры и памяти виртуальной машины Java. Если есть какие -либо неуместные слова или неточные объяснения, пожалуйста, обязательно поправьте их. Для меня большая честь!
Архитектура виртуальной машины Java
Загрузка класса подсистема
Для виртуальных машин Java существует два класса погрузчиков, а именно погрузчик класса запуска и пользовательский погрузчик.
Загрузка класса подсистема загружает класс в область данных времени выполнения через полностью квалифицированное имя класса (имя пакета и имя класса, монтирование сети также включает URL). Для каждого загруженного типа виртуальная машина Java создает экземпляр класса java.lang.class для представления типа, который находится в области кучи в памяти, а информация о загруженном типе находится в области метода, которая совпадает с всеми другими объектами.
Перед загрузкой типа подсистема загрузки класса должна не только найти и импортировать соответствующий двоичный файл класса, но и проверять правильность импортированного класса, выделять и инициализировать память для переменных класса и ссылки на символы в качестве прямых ссылок. Эти действия строго в следующем порядке:
1) Загрузка - найти и загрузить двоичные данные типа;
2) Соединение - выполнить проверку, подготовку и анализ (необязательно)
3) Убедитесь, что убедитесь, что правильность импортируемого типа
4) Подготовьтесь к распределению памяти для переменных класса и инициализируйте их по значениям по умолчанию
5) Проанализируйте символическую ссылку в типе к прямому применению
Метод зона
Для каждого типа, загруженного подсистемой загрузки класса, виртуальная машина сохраняет следующие данные в области метода:
1. Полностью квалифицированное название типа
2. Полностью квалифицированное название типа Superclass (java.lang.object не имеет Superclass)
3. - это тип класса типа или тип интерфейса
4. Введите модификатор доступа
5. Полностью квалифицированное имя заказано в списке любой прямой гипертерфейной
В дополнение к вышеуказанной информации о базовом типе, также будет сохранена следующая информация:
6. Тип постоянный бассейн
7. Информация о поле (включая имя поля, тип поля, модификатор поля)
8. Информация о методе (включая имя метода, тип возврата, номер и тип параметров, модификаторы метода. Если метод не является абстрактным и локальным, то также будут сохранены таблица метода, стек операнда и таблица размеров и исключений локальной переменной в рамке стека методов).
9. Все переменные класса, кроме константов (на самом деле, они являются статическими переменными класса. Поскольку статические переменные разделяются во всех экземплярах и напрямую связаны с типом, они являются переменными на уровне класса и сохраняются в области метода в качестве членов класса)
10. Ссылка на загрузчик класса
// возвращаемый является эталонной string string.class.class.getClassloader (), который был сохранен только сейчас; Ссылка на класс класса // он вернет эталонный string.class класса класса, только что сохраненный только сейчас;
Обратите внимание, что область метода также может быть переработана сборщиком мусора.
куча
Все экземпляры класса или массивы, созданные программами Java во время выполнения, помещаются в одну и ту же кучу, и на каждой виртуальной машине Java также есть место кучи, и все потоки имеют кучу (именно поэтому многопоточная программа Java вызовет проблемы синхронизации в доступе объекта).
Поскольку каждая виртуальная машина Java имеет различные реализации спецификации виртуальной машины, мы можем не знать, какую форму каждая виртуальная машина Java представляет экземпляры объекта в куче, но мы можем увидеть следующие возможные реализации:
Программная счетчик
Для запуска программ Java каждый поток имеет свой собственный регистр ПК (счетчик программ), который создается, когда поток запускается, с размером одного слова, и используется для сохранения местоположения следующей строки кода, которую необходимо выполнить.
Java Stack
В каждом потоке есть стек Java, который сохраняет рабочее состояние потока в единицах кадров стека. На стеке Java есть два типа операций виртуальных машин: нажатие и укладку стека, оба из которых имеют кадры. Камень стека сохраняет данные, такие как входящие параметры, локальные переменные, результаты промежуточной работы и т. Д., Которые выскочивают, когда метод завершен и затем освобожден.
Взгляните на снимок памяти кадры стека, когда две локальные переменные складываются вместе
Локальный стек методов
Именно здесь Java вызывает локальную библиотеку операционной системы, используемая для реализации JNI (нативный интерфейс Java, локальный интерфейс Java)
Двигатель выполнения
Ядро виртуальной машины Java управляет загрузкой байт -кода Java и диапазоном; Для запуска программ Java каждый поток является экземпляром независимого механизма выполнения виртуальной машины. С начала до конца жизненного цикла потока он либо выполняет байт -код, либо выполняет локальные методы.
Локальный интерфейс
Подключено к локальному стеку методов и библиотеке операционной системы.
Примечание. Все места, упомянутые в статье, относятся к «спецификации виртуальной машины Java для платформ Javaee и Javase».
Практика оптимизации памяти виртуальной машины
Поскольку упоминается память, необходимо упомянуть утечки памяти. Как мы все знаем, Java разработала на основе C ++, и большая проблема с программами C ++ заключается в том, что утечки памяти трудно решить. Несмотря на то, что JVM в Java имеет свой собственный механизм сбора мусора для переработки памяти, во многих случаях разработчикам программы Java не нужно слишком беспокоиться, но есть и проблемы с утечкой, которые немного меньше, чем C ++. Например, в программе существует ссылка, но бесполезный объект: если программа ссылается на объект, но не будет или не может использовать его в будущем, то пространство памяти, которую она занимает, будет потрачено впустую.
Давайте сначала рассмотрим, как работает GC: контролируйте статус выполнения каждого объекта, включая приложение, цитирование, цитирование, назначение и т. Д. Когда объект больше не цитируется, выпустите объект (фокус GC Эта статья не будет объяснена слишком много). Многие программисты Java слишком сильно полагаются на GC, но ключ к проблеме заключается в том, что независимо от того, насколько хорош механизм сбора мусора JVM, память всегда является ограниченным ресурсом. Поэтому, даже если GC завершит большую часть сбора мусора для нас, все еще необходимо соответствующим образом обратить внимание на оптимизацию памяти во время процесса кодирования. Это может эффективно снизить количество GC, одновременно улучшая использование памяти и максимизируя эффективность программы.
В целом, оптимизация памяти виртуальных машин Java должна начинаться с двух аспектов: виртуальные машины Java и приложения Java. Первое относится к управлению размером раздела логической памяти виртуальной машины через параметры виртуальной машины в соответствии с проектированием приложения, чтобы память виртуальной машины дополняла требования памяти программы; Последнее относится к оптимизации алгоритмов программы, снижению бремени GC и повышению успеха утилизации GC.
Параметры для оптимизации памяти виртуальной машины посредством параметров следующие:
XMS
Начальный размер кучи
XMX
максимальное значение кучи Java
1 млн
Размер молодого поколения
XSS
Размер стека для каждого потока
Выше приведено на три более часто используемых параметров, некоторые:
XX: Minheapfreeratio = 40
Минимальный процент свободной от кучи после GC, чтобы избежать расширения.
XX: maxheapfreeratio = 70
Максимальный процент свободной от кучи после GC, чтобы избежать сокращения.
XX: Newratio = 2
Соотношение размеров нового/старого поколения. [Sparc -client: 8; x86 -server: 8; x86 -client: 12.] -Клиент: 8 (1.3.1+), x86: 12]
XX: Newsize = 2,125 м
Размер по умолчанию нового поколения (в байтах) [5,0 и более новее: 64 -битные виртуальные машины масштабируются на 30% больше; x86: 1M; x86, 5,0 и старше: 640K]
Xx: maxnewsize =
Максимальный размер нового поколения (в байтах). С 1.4 MaxNewSize вычисляется как функция NewRatio.
XX: Survivivorratio = 25
Соотношение размера пространства Eden/Survivor [SPARC в 1.3.1: 25; Другие платформы Solaris в 5,0 и ранее: 32]
Xx: permsize =
Начальный размер постоянного поколения
XX: maxpermsize = 64 м
Размер постоянного поколения. [5,0 и более новее: 64 -битные виртуальные машины масштабируются на 30% больше; 1.4 AMD64: 96M; 1.3.1 -client: 32m.]
То, что упоминается ниже, для улучшения использования памяти и снижения рисков памяти путем оптимизации алгоритмов программы является совершенно эмпирическим и только для справки. Если есть неуместность, пожалуйста, поправьте меня, спасибо!
1. Отпустите ссылку на бесполезные объекты как можно скорее (xx = null;)
Посмотрите на кусок кода:
public List <pagegatata> parse (htmlpage page) {list <pagegatata> list = null; try {list valuelist = page.getbyxpath (config.getContentxPath ()); if (valuelist == null || valuelist.isempty ()) {return List; } // Создать объект, когда это необходимо, сохранить память и улучшить список эффективности = new ArrayList <PagegeTata> (); Pagegatata pagegatata = new Pagegata (); StringBuilder value = new StringBuilder (); for (int i = 0; i <valuelist.size (); i ++) {htmlelement content = (htmlelement) valuelist.get (i); Domnodelist <htmlelement> imgs = content.getelementsbytagname ("img"); if (imgs! = null &&! imgs.isempty ()) {for (htmlelement img: imgs) {try {htmlimage image = (htmlimage) img; String path = image.getSrcattribute (); String format = path.substring (path.lastindexof ("."), Path.length ()); String localpath = "d:/images/" + md5helper.md5 (path) .replace ("//", ","). Заменить ("/", ",") + format; File localfile = новый файл (localPath); if (! localfile.exists ()) {localfile.createnewFile (); Image.saveas (localfile); } image.setattribute ("src", "file: ////" + localpath); localfile = null; Image = null; img = null; } catch (Exception e) {}} // Этот объект не будет использоваться в будущем. Очистка ссылки на нее эквивалентно сообщению GC заранее. Объект может переработать imgs = null; } String text = content.asxml (); value.append (text) .append ("<br/>"); valuelist = null; Содержание = NULL; текст = null; } pagegatata.setContent (value.toString ()); PageData.SetcharSet (page.getPageEncoding ()); list.add (pagegata); // Pagegatata = null; бесполезен, потому что список по -прежнему содержит ссылку на объект, а GC не будет переработать его значение = null; // Здесь нет списка = null; Поскольку список - это возвращаемое значение метода, в противном случае возвратное значение, которое вы получаете из метода, всегда будет пустым, и такого рода ошибки нелегко обнаружить или исключить} Catch (Exception e) {} return List; }2. Тщательно используйте типы данных сбора, например, массивы, деревья, графики, связанные списки и другие структуры данных. Эти структуры данных более сложны для переработки для GC.
3. Избегайте явного применения на место для массива. Когда вам нужно явно применить, постарайтесь оценить ее разумное значение как можно точнее.
4. Старайтесь избегать создания и инициализации большого количества объектов в конструкторе класса по умолчанию и предотвратить ненужные отходы ресурсов памяти при вызове своего собственного конструктора класса.
5. Старайтесь избегать принудительной системы для переработки мусорной памяти и увеличить последнее время переработки мусора в системе
6. Попробуйте использовать переменные мгновенного значения при разработке приложений удаленного метода вызовов, если только удаленный абонент не должен получить значение переменной мгновенного значения.
7. Постарайтесь использовать технологию объединения объектов в соответствующих сценариях для повышения производительности системы