Оптимизация кучи и памяти
Сегодня я проверил функцию автоматической сортировки данных проекта, разбирая десятки тысяч записей и изображений в базе данных. Когда операция была близка к концу, java.lang.outofmemoryerror, была обнаружена ошибка в пространстве кучи Java. В прошлом я редко сталкивался с такими ошибками памяти в письменных программах, потому что у Java есть механизм коллекционера мусора, поэтому я не обращал на это особого внимания. Сегодня я искал некоторую информацию в Интернете и отсортировал ее на этой основе.
1. Степение и стек
Построенный из кучи с новым, коллекционер мусора отвечает за переработку
1. Когда программа начинает работать, JVM получает память от ОС, часть которой является память кучи. Хиповая память обычно расположена вверх в нижней части адреса хранения.
2. Куча представляет собой область данных «время выполнения», а объект, созданный в классе, выделяет пространство из кучи;
3. Распределение пространства на кучу устанавливается с помощью таких инструкций, как «новый». Куча - это динамически распределенный размер памяти, и срок службы не нужно сообщать компилятору заранее;
4. В отличие от C ++, Java автоматически управляет кучей и стеком, а сборщик мусора может автоматически перерабатывать память кучи, которая больше не используется;
5. Недостатком является то, что, поскольку память динамически распределяется во время выполнения, скорость доступа к памяти медленнее.
Стек - хранить основные типы и ссылки, быстрые
1. Первая в In, а затем структура данных обычно используется для сохранения параметров и локальных переменных в методе;
2. В Java все переменные базовых типов (короткие, int, long, byte, float, double, boolean, char) и эталонные типы хранятся в стеке;
3. Живое пространство данных в стеке, как правило, находится в текущих областях (область, заключенная на {...};
4. Скорость доступа стека быстрее, чем куча, уступая только регистрам, расположенным непосредственно в процессоре;
5. Данные в стеке могут быть переданы, и несколько ссылок могут указывать на один и тот же адрес;
6. Недостатком является то, что размер данных и время жизни стека должны быть определены и не имеют гибкости.
2. Настройки памяти
1. Проверьте состояние памяти виртуальной машины
long maxcontrol = runtime.getRuntime (). MaxMemory (); // Получить максимальное количество памяти, которую виртуальная машина может управлять Long Currentuse = Runtime.getRuntime (). TotalMemory (); // Получить количество памяти, используемой в настоящее время виртуальной машиной
По умолчанию MaxControl = 66650112b = 63,5625 м виртуальной машины Java;
Если вы ничего не делаете, CurrentUSE измерен на моей машине = 5177344B = 4,9375 м;
2. Команда установить размер памяти
-Xms <размер> Установите начальный размер кучи Java: установите размер памяти инициализации JVM; Это значение может быть установлено так же, как -xmx, чтобы избежать памяти перераспределения JVM каждый раз, когда сборы мусора завершаются.
-Xmx <Размер> Установите максимальный размер кучи Java: установите максимальный размер памяти кучи JVM;
-Xmn <Размер>: Установите размер молодого поколения, весь размер кучи = размер молодого поколения + размер старого поколения + размер последнего поколения.
-Xss <Размер> Установите размер стека потока Java: Установите размер памяти стека потока JVM;
3. Конкретные операции (1) Настройки памяти JVM:
Откройте Myeclipse (Eclipse) Windows-Preferences-Java-установленные аргументы jres-edit-default
Enter: -xmx128m -xms64m -xmn32m -xss16m
(2) Настройки памяти IDE:
Измените конфигурацию в рамках -vmargs в myeclipse.ini (или eclipse.ini в корневом каталоге Eclipse):
(3) Настройки памяти Tomcat
Откройте папку бина в корневом каталоге Tomcat и Edit Catalina.bat
Modify to: set java_opts = -xms256m -xmx512m
3. Анализ ошибок OutofmemoryError
Когда JVM запускается, используется память кучи, установленное параметром -xms. Поскольку программа продолжается и создает больше объектов, JVM начинает расширять память кучи, чтобы удерживать больше объектов. JVM также использует сборщик мусора для переработки памяти. Когда максимальная память куча, установленная -xmx, почти достигается, если больше не может быть выделена памяти на новый объект, JVM бросит java.lang.outofmemoryerror, и программа будет сбой. Прежде чем бросить OutofmemoryError, JVM попытается освободить достаточно места с коллекционером мусора, но выбросит эту ошибку, когда обнаружит, что места все еще недостаточно. Чтобы решить эту проблему, вам необходимо прояснить информацию об объектах программы, например, какие объекты создали, какие объекты занимают, сколько места и т. Д. Вы можете использовать профилировщик или анализатор кучи для обработки ошибок OutofmemoryError. «java.lang.outofmemoryerror: пространство кучи Java» означает, что куча не имеет достаточно места и не может продолжать расширяться. «java.lang.outofmemoryerror: Permen Space» означает, что постоянное поколение заполнено, и ваша программа больше не может загружать класс или выделять строку.
4. Сбор кучи и мусора
Мы знаем, что объекты создаются в памяти кучи, сбор мусора - это процесс, который очищает мертвые объекты из места кучи и возвращает эту память в кучу. Чтобы использовать сборщик мусора, куча в основном разделена на три области, а именно новое поколение, старое поколение или постоянное поколение и пространство PRM. Новое поколение - это пространство, используемое для хранения вновь созданных объектов, и используется, когда объект создан вновь. При использовании в течение долгого времени они будут перемещены в старое поколение (или штатное поколение) сборщиком мусора. Пространство Perm-это то, где JVM хранит метаданные, такие как классы, методы, пулы струн и детали на уровне класса.
5. Резюме:
1. Память кучи Java является частью памяти, выделенной операционной системе JVM.
2. Когда мы создаем объекты, они хранятся в памяти Java Heap.
3. Чтобы облегчить сбору мусора, пространство кучи Java разделено на три области, называемые новым поколением, старым поколением или штатным поколением, и PRMP Space.
4. Вы можете настроить размер пространства кучи Java, используя параметры командной строки JVM -XMS, -xmx и -xmn.
5. Вы можете использовать jconsole или runtime.maxmemory (), runtime.totalmemory () и runtime.freememory (), чтобы просмотреть размер памяти кучи в Java.
6. Вы можете использовать команду «jmap», чтобы получить свалку кучи, и использовать «jhat», чтобы проанализировать дамп кучи.
7. Пространство на куче Java отличается от места стека. Пространство стека используется для хранения стеков вызовов и локальных переменных.
8. Коллекционер мусора Java используется для восстановления памяти, занятой мертвыми объектами (объектами, которые больше не используются), и освободить ее в пространство кучи Java.
9. При столкновении с java.lang.outofmemoryerror, вам не нужно беспокоиться. Иногда вам просто нужно увеличить пространство кучи. Но если это происходит часто, вы должны посмотреть, есть ли утечка памяти в программе Java.
10. Используйте инструменты анализа Profiler и Hup Damp, чтобы просмотреть пространство кучи Java, и вы можете увидеть, сколько памяти выделяется каждому объекту.
Подробное объяснение хранения стека
Java Stack Storage имеет следующие характеристики:
1. Должен быть определен размер данных и жизненный цикл в стеке.
Например, хранение основного типа: int a = 1; Эта переменная содержит буквальное значение, A - это ссылка на тип Int, указывающий на буквальное значение 3. Из -за размера и срока службы этих буквальных данных эти буквальные значения определяются в программном блоке, а после выхода программного блока исчезают буквальные значения), существуют в стеке для самого преследования скорости.
2. Данные, существующие в стеке, могут быть обмен.
(1) Хранение данных базового типа:
нравиться:
int a = 3; int b = 3;
Компилятор сначала обрабатывает int a = 3; Сначала он создаст ссылку на переменную A в стеке, а затем выяснит, есть ли адрес с буквальным значением 3. Если она не найдена, он откроет адрес с буквальным значением 3, а затем укажет A на адрес 3. Затем процесс int b = 3; После создания эталонной переменной B, поскольку в стеке уже есть буквальное значение 3, B напрямую указывает на адрес 3. Таким образом, A и B оба указывают на 3 одновременно.
Примечание. Эта буквальная ссылка отличается от ссылок на объекты класса. Предполагая, что ссылки двух объектов класса указывают на объект одновременно, если одна ссылка объекта изменяет внутреннее состояние объекта, то другая эталонная переменная объекта немедленно отражает это изменение. Вместо этого изменение его значения с помощью буквальной ссылки не приведет к соответствующему изменению другого значения. Как и в приведенном выше примере, после того, как мы определим значения A и B, пусть A = 4; Затем B не будет равен 4 или равна 3. Внутри компилятора, когда встречается a = 4, он будет повторно поиск, есть ли буквальное значение 4 в стеке. Если нет, повторно откройте адрес для хранения значения 4; Если это уже существует, напрямую укажите А на этот адрес. Следовательно, изменение значения А не повлияет на значение b.
(2) Хранение данных упаковки:
Классы, которые обертывают соответствующие основные типы данных, такие как целое число, двойная, строка и т. Д. Все эти данные класса существуют в куче. Java использует новый () оператор () для отображения компилятора и создает динамически только по мере необходимости во время выполнения, поэтому он более гибкий, но недостаток в том, что это занимает больше времени.
Например: возьмите строку в качестве примера.
Строка - это специальные данные упаковки. То есть он может быть создан в форме строки str = new String ("ABC"); или это может быть создано в форме строки str = "abc";. Первый - это стандартизированный процесс создания класса, то есть в Java, все является объектом, а объект является экземпляром класса, все созданные в форме New (). Некоторые классы в Java, такие как класс DateFormat, могут вернуть недавно созданный класс через метод GetInstance () класса, который, по -видимому, нарушает этот принцип. На самом деле, это не так. Этот класс использует шаблон Singleton для возврата экземпляра класса, но этот экземпляр создается внутри класса через New (), который скрывает эту деталь извне.
Тогда почему экземпляр не создается через new () в string str = "abc";? Это нарушается вышеуказанный принцип? На самом деле, нет.
О внутренней работе строки Str = "ABC". Java Внутренне превращает это утверждение в следующие шаги:
а Сначала определите эталонную переменную объекта с именем STR в классе String: String Str;
беременный Узнайте, есть ли адрес со значением «ABC» в стеке. Если нет, откройте адрес с буквальным значением «ABC», затем создайте новый объект o st String Class и укажите значение o o на этот адрес и обратите внимание на справочный объект o рядом с этим адресом в стеке. Если есть адрес со значением «ABC», ищите объект O и верните адрес O.
в Точка ST по адресу объекта O.
Стоит отметить, что обычно строковые значения в классе строки хранятся напрямую. Но в таких ситуациях, как string str = "abc";, его строковое значение содержит ссылку на данные, присутствующие в стеке (то есть: string str = "abc"; как хранилище стека, так и хранилище кучи).
Чтобы лучше проиллюстрировать эту проблему, мы можем проверить ее через следующие коды.
String str1 = "abc"; String str2 = "abc"; System.out.println (str1 == str2); //истинный
(Значение истины возвращается только в том случае, если оба ссылки указывают на один и тот же объект. Являются ли Str1 и STR2, указывающие на один и тот же объект)
Результат показывает, что JVM создал две ссылки Str1 и STR2, но был создан только один объект, и обе ссылки указывали на этот объект.
String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; System.out.println (str1 + "," + str2); // bcd, abc System.out.println (str1 == str2); //ЛОЖЬ
Это означает, что изменение назначения приводит к изменению ссылки на объект класса, STR1 указывает на другой новый объект, в то время как STR2 все еще указывает на исходный объект. В приведенном выше примере, когда мы меняем значение STR1 на «BCD», JVM обнаружил, что нет адреса для хранения значения в стеке, поэтому он открыл этот адрес и создал новый объект, чье строковое значение указывает на этот адрес.
На самом деле, класс строки предназначен для того, чтобы быть неизменным классом. Если вы хотите изменить его значение, вы можете, но JVM тихо создает новый объект, основанный на новом значении во время выполнения (его нельзя изменить на основе исходной памяти), а затем возвращает адрес этого объекта к ссылке исходного класса. Хотя этот процесс создания является полностью автоматическим, в конце концов он занимает больше времени. В среде, которая более чувствительна к требованиям времени, это будет иметь определенные побочные эффекты.
String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; String str3 = str1; System.out.println (str3); // bcd string str4 = "bcd"; System.out.println (str1 == str4); //истинный
Ссылка на объект STR3 непосредственно указывает на объект, на который указывает Str1 (обратите внимание, что STR3 не создает новый объект). После того, как STR1 изменил свое значение, создайте ссылку на строку STR4 и укажите на новый объект, созданный STR1, изменяющий значение. Можно обнаружить, что на этот раз STR4 не создавал новый объект, тем самым снова реализуя обмен данными в стеке.
String str1 = new String ("ABC"); String str2 = "abc"; System.out.println (str1 == str2); //ЛОЖЬБыло создано две ссылки. Были созданы два объекта. Две ссылки указывают на два разных объекта.
String str1 = "abc"; String str2 = new String ("ABC"); System.out.println (str1 == str2); //ЛОЖЬБыло создано две ссылки. Были созданы два объекта. Две ссылки указывают на два разных объекта.
В приведенных выше двух кодах указывается, что до тех пор, пока объект создается с помощью New (), он будет создан в куче, а его строки хранятся отдельно. Даже если они такие же, как данные в стеке, они не будут переданы данными в стеке.
Суммировать:
(1) Когда мы определяем класс, используя формат, такой как string str = "abc";, мы всегда считаем само собой разумеющимся, что мы создали объект Str String Class. Беспокоитесь о ловушке! Объект, возможно, не был создан! Единственное, что является уверенным, это то, что создается ссылка на класс строки. Что касается того, указывает ли эта ссылка на новый объект, он должен рассматриваться на основе контекста, если только вы не используете метод нового () для явного создания нового объекта. Следовательно, чтобы быть более точным, мы создаем эталонную переменную STR для объекта класса строки, который указывает на класс строки со значением «ABC». Знать об этом полезно для устранения неполадок в программах.
(2) используя string str = "abc"; может в определенной степени улучшить скорость работы программы, потому что JVM автоматически решает, необходимо ли создать новый объект на основе фактической ситуации данных в стеке. Для кода строки str = new String ("ABC");, новые объекты создаются в куче независимо от того, равны ли их строки или нет, необходимы ли это для создания новых объектов, тем самым увеличивая бремя на программе.
(3) Из -за неизменной природы класса строкости (поскольку значение класса обертки не может быть изменено), когда направленная переменная необходимо часто преобразовать, класс StringBuffer следует учитывать для повышения эффективности программы.