Как устроена память в java
JRE кратко - для работы. Java Runtime Environment (сокр. JRE) - минимальная реализация виртуальной машины, необходимая для исполнения Java-приложений, без компилятора и других средств разработки. Состоит из виртуальной машины - Java Virtual Machine и библиотеки Java-классов.
JDK кратко - для программирования. Java Development Kit (сокращенно JDK) - бесплатно распространяемый компанией Oracle Corporation (ранее Sun Microsystems) комплект разработчика приложений на языке Java, включающий в себя компилятор Java (javac), стандартные библиотеки классов Java, примеры, документацию, различные утилиты и исполнительную систему Java (JRE).
Java Virtual Machine (сокращенно Java VM, JVM) - виртуальная машина Java - основная часть исполняющей системы Java, так называемой Java Runtime Environment (JRE). Виртуальная машина Java интерпретирует Байт-код Java, предварительно созданный из исходного текста Java-программы компилятором Java (javac). JVM может также использоваться для выполнения программ, написанных на других языках программирования.
- private: (используется конструкторах, внутренних классах, методах и полях класса) - Доступ разрешен только в текущем классе.
- default (package-private): (используется в классах, конструкторах, интерфейсах, внутренних классах, методах и полях класса) - Доступ на уровне пакета. Если класс будет так объявлен он будет доступен только внутри пакета.
- protected: (используется конструкторах, внутренних классах, методах и полях класса) Модификатор доступа на уровне пакета и в иерархии наследования.
- public: (используется в классах, конструкторах, интерфейсах, внутренних классах, методах и полях класса) - Модификатор доступа общественный, доступен всем.
Последовательность модификаторов по убыванию уровня закрытости: private, default ,protected, public).
Доступ из классов одного package-а в классы другого package-a.
- Чем абстрактный клас отличается от интерфейса? В каких случаях Вы бы использовали абстрактный класс, а в каких интерфейс?
Абстрактный класс это класс, который помечен как "abstract", он может содержать абстрактные методы, а может их и не содержать.
Класс, который наследуется от абстрактного класса может реализовывать абстрактные методы, а может и не реализовывать, тогда класс наследник должен быть тоже абстрактным. Также если класс наследник переопределяет реализованный в абстрактном классе родители метод, его можно переопределить с модификатором абстракт! Т.е отказаться от реализации. Соответственно данный класс должен быть также абстрактным также.
- Default метод в интерфейсе - это метод в интерфейсе с по умолчанию реализованной логикой, который не требуется обязательно определять в реализации этого интерфейса.
- Static методы в интерфейсе - это по существу то же самое, что static-методы в абстрактном классе.
Абстрактный класс используется когда нам нужна какая-то реализация по умолчанию. Интерфейс используется когда классу нужно указать конкретное поведение. Часто интерфейс и абстрактный класс комбинируют, т.е. имплементируют интерфейс в абстрактном классе, чтоб указать поведение и реализацию по умолчанию. Это хорошо видно на примере свига:
Мы создаем свою модель таблицы с определенным поведением и уже с реализацией по умолчанию.
ВАЖНО! При реализации интерфейса, необходимо реализовать все его методы, иначе будет Fatal error, так же это можно избежать, присвоив слово abstract.
- Может ли объект получить доступ к private-переменной класса? Если, да, то каким образом?
Вообще доступ у приватной переменной класса можно получить только внутри класса, в котором она объявлена. Также доступ к приватным переменным можно осуществить через механизм Java Reflection API.
Статические блоки в джава выполняются до выполнения конструктора, с помощью них инициализируют статические поля к примеру.
Еще один ньюанс, блок статической инициализации может создаваться сам при компиляции программы:
Статические методы могут перегружаться нестатическими и наоборот - без ограничений. А вот в переопределении статического метода смысла нет.
- Расскажите про внутренние классы. Когда вы их будете использовать?
Внутренний класс - это класс, который находится внутри класса или интерфейса. При этом он получает доступ ко всем полям и методам своего внешнего класса.
Для чего он может применятся? Например чтоб обеспечить какую-то дополнительную логику класса. Хотя использование внутренних классов усложняет программу, рекомендуется избегать их использование.
- В чем разница между переменной экземпляра и статической переменной? Приведите пример.
Статические переменные инициализируются при загрузке класса класслодером, и не зависят от объекта. Переменная экземпляра инициализируется при создании класса.
Пример: Например нам нужна глобальная переменная для всех объектов класс, например число посещений пользователей определенной статьи в интернете. При каждом новом посещении статьи создается новый объект и инкрементируется переменная посещений.
- Приведите пример когда можно использовать статический метод?
Статические методы могут быть использованы для инициализации статических переменных. Часто статические методы используются в классах утилитах, таких как Collections, Math, Arrrays
- Расскажите про классы- загрузчики и про динамическую зарузку классов.
Любой класс, используемый в джава программу так или иначе был загружен в контекст программы каким-то загрузчиком.
Все виртуальные машины джава включают хотябы один загрузчик классов, так называем базовый загрузчик. Он загружает все основные классы, это классы из rt.jar. Интересно то, что этот загрузчик никак не связан с программой, тоесть мы не можем получить например у java.lang.Object имя зарузчика, метод getClassLoader() вернет нам null.
Следующий загрузчик - это загрузчик расширений, он загружает классы из $JAVA_HOME/lib/ext.
Далее по иерархии идет системный загрузчик, он загружает классы, путь к которым указан в переменно класпас.
Сначала системный загрузчик пытается найти его в своем кэше загрузок его, если найден - класс успешно загружается, иначе управление загрузкой передается загрузчику расширений, он также проверяет свой кэш загрузок и в случае неудачи передает задачу базовому загрузчику. Тот проверяет кэш и в случае неудачи пытается его загрузить, если загрузка прошла успешно - загрузка закончена. Если нет - передает управление загрузчику расширений. Загрузчик расширений пытается загрузить класс и в случае неудачи передает это дело системному загрузчику. Системный загрузчик пытается загрузить класс и в случае неудачи возбуждается исключение java.lang.ClassNotFoundException.
Вот так работает загрузка классов в джава. Так называемое делегирование загрузки.
Если в системе присутствуют пользовательские загрузики, то они должны быть унаследованы от класса java.lang.ClassLoader .
Что же такое статическая и что такое динамическая загрузка класса?
Статическая загрузка класса происходит при использовании оператора "new".
Динамическая загрузка происходит "на лету" в ходе выполнения программы с помощью статического метода класса Class.forName(имя класса). Для чего нужна динамическая загрузка? Например мы не знаем какой класс нам понадобится и принимаем решение в ходе выполнения программы передавая имя класса в статический метод forName().
Это так называемый оператор утверждений. Он проверяет некое условие, если оно ложно, то генерируется AssertationError
- Почему в некоторых интерфейсах вообще не определяют методов?
Это так называемые интерфейсы - маркеры. Они просто указывают что класс относится к определенной группе классов. Например интерфейс Clonable указывает на то, что класс поддерживает механизм клонирования.
Степень абстракции в данном случае доведен до абсолюта. В интерфейсе вообще нет никаких объявлений.
- Searilizable interface
- Cloneable interface
- Remote interface
- ThreadSafe interface
- Какая основная разница между String, StringBuffer, StringBuilder?
String - неизменяемый класс, тоесть для для добавление данных в уже существующую строку, создается новый объект строки.
StringBuffer и StringBuilder могут изменятся и добавление строки не такое дорогостоющее с точки зрения памяти. Первы - синхронизированный, второй - нет. Это их единственное различие.
Правда если нам нужно сделать подстроку строки, то лучше использовать String, так как ее массив символов не меняется и не создается заново для новой строки. А вот в StringBuffer и StringBuilder для создания подстроки создается новый массив символов.
- байтовый поток(InputStream и OutputStream);
- символный поток(Reader и Writer);
Это все абстрактные классы - декораторы, которым можно добавлять дополнительный функционал, например:
InputStream in = new FileInputStream(new File("file.txt"));
Java Heap (куча) - динамически распредляемая область памяти, создаваемая при старте JVM. Используется Java Runtime для выделения памяти под объекты и JRE классы. Создание нового объекта также происходит в куче. Здесь работает сборщик мусора: освобождает память путем удаления объектов, на которые нет каких-либо ссылок. Любой объект, созданный в куче, имеет глобальный доступ и на него могут ссылаться с любой части приложения.
- Все обьекты обитают в куче и попадают туда при создании.
- обьект состоит из полей класса и методов.
- в куче выделяется место под сам обьект, количество выделенной памяти зависит от полей, если у тебя полем класса, к примеру, служит интовая переменная, то не важно, инициализируешь ты ее как "0" или как "1000000" - обьект займет в куче свои биты, + столько байт сколько вмещает тип int(+32 бита), и так с каждым полем.
Стековая память в Java работает по схеме LIFO (Последний-зашел-Первый-вышел). Всякий раз, когда вызывается метод, в памяти стека создается новый блок, который содержит примитивы и ссылки на другие объекты в методе расположение в RAM и достижение процессору через указатель стека. Как только метод заканчивает работу, блок также перестает использоваться, тем самым предоставляя доступ для следующего метода. Размер стековой памяти намного меньше объема памяти в куче.
- Все методы обитают в стеке и попадают туда при вызове.
- Переменные в методах так же имеют стековую память, по скольку они локальные.
- Если в методе создается обьект, то он помещается в кучу, но его ссылка все еще будет находится в стеке и после того как метод покинет стек - обьект станет жертвой сборщика мусора, так как ссылка на него утеряна, и из главного стека программы невозможно будет добраться до такого обьекта.
- Какая разница между Stack и Heap памятью в Java?
Приведем следующие различия между Heap и Stack памятью в Java.
- Куча используется всеми частями приложения в то время как стек используется только одним потоком исполнения программы.
- Всякий раз, когда создается объект, он всегда хранится в куче, а в памяти стека содержится ссылка на него. Память стека содержит только локальные переменные примитивных типов и ссылки на объекты в куче.
- Объекты в куче доступны с любой точки программы, в то время как стековая память не может быть доступна для других потоков.
- Управление памятью в стеке осуществляется по схеме LIFO.
- Стековая память существует лишь какое-то время работы программы, а память в куче живет с самого начала до конца работы программы.
- Мы можем использовать -Xms и -Xmx опции JVM, чтобы определить начальный и максимальный размер памяти в куче. Для стека определить размер памяти можно с помощью опции -Xss .
- Если память стека полностью занята, то Java Runtime бросает java.lang.StackOverflowError, а если память кучи заполнена, то бросается исключение java.lang.OutOfMemoryError: Java Heap Space.
- Размер памяти стека намного меньше памяти в куче. Из-за простоты распределения памяти (LIFO), стековая память работает намного быстрее кучи.
В Джаве память устроена следующим образом, есть два вида:
- Eden(назовем ее первая) куча
- Survival(выжившая) куча
- Как работает сборщик мусора (garbage collector)?
Во-первых что стоит сказать, что у сборщика мусора есть несколько алгоритмов работы, он не один.
Ситуация другая, в первой кучи мало мусора, но очень много рабочих объектов. Как поступает в этом случае сборщик мусора?
Он помечает мусор, удаляет его и оставшиеся объекты компонует.
Также следует заметить что при нехватке места в Выжившей куче, объекты переносятся в старую кучу, там хранятся как правило долго живущие объекты.
Также следует заметить что сборщик мусора вызывается сам периодически, а не только когда памяти не хватает.
- Расскажите про приведение типов. Что такое понижение и повышение типа? Когда вы получаете ClassCastException?
Приведение типов это установка типа переменной или объекта отличного от текущего. В ждава есть два вида приведения:
тоесть если мы расширяем тип, то явное преобразование не требуется, приведение происходит автоматически. Если же мы сужаем, то необходимо явно указывать приведение типа.
В случае же с объектами, то мы можем сделать автоматическое приведение от наследника к родителю, но никак не наоборот, тогда вылетит ClassCastException.
- Что такое статический класс, какие особенности его использования?
Статическим классом может быть только внутренний клас(определение класса размещается внутри другого класса). В объекте обычного внутреннего класса хранится ссылка на объект внешнего класса. Внутри статического внутреннего класса такой ссылки нет.
То есть: Для создания объекта статического внутреннего класса не нужен объект внешнего класса. Из объекта статического вложенного класса нельзя обращаться к нестатическим членам внешнего класса напрямую. И еще обычные внутренние классы не могут содержать статические методы и члены.
- Каким образом из вложенного класса получить доступ к полю внешнего класса.
Если класс внутренний то: Внешнийкласс.this.Поле внешнего класса Если класс статический внутренний(вложенный),то в методе нужно создать объект внешнего класса, и получить доступ к его полю.Или второй вариант объявить это поле внешнего класса как static
- Какие существуют типы вложенных классов? Для чего они используются?
Вложенные классы существуют внутри других классов. Нормальный класс - полноценный член пакета. Вложенные классы, которые стали доступны начиная с Java 1.1, могут быть четырех типов:
- статические члены класса
- члены класса
- локальные классы
- анонимные классы
Статические члены классов (static nested classes) - как и любой другой статический метод, имеет доступ к любым статическим методам своего внешнего класса, в том числе и к приватным. К нестатическим полям и методам обрамляющего класса он не может обращатся напрямую. Он может использовать их только через ссылку на экземпляр класса родителя.
Члены класса - локальные классы, объявленные внутри блока кода. Эти классы видны только внутри блока.
Анонимные классы - Эти типы классов не имеют имени и видны только внутри блока.
Привет, Хабр! Представляю вашему вниманию перевод первой части статьи «Java Memory Model» автора Jakob Jenkov.
Прохожу обучение по Java и понадобилось изучить статью Java Memory Model. Перевёл её для лучшего понимания, ну а чтоб добро не пропадало решил поделиться с сообществом. Думаю, для новичков будет полезно, и если кому-то понравится, то переведу остальное.
Первоначальная Java-модель памяти была недостаточно хороша, поэтому она была пересмотрена в Java 1.5. Эта версия модели все ещё используется сегодня (Java 14+).
Java-модель памяти, используемая внутри JVM, делит память на стеки потоков (thread stacks) и кучу (heap). Эта диаграмма иллюстрирует Java-модель памяти с логической точки зрения:
Каждый поток, работающий в виртуальной машине Java, имеет свой собственный стек. Стек содержит информацию о том, какие методы вызвал поток. Я буду называть это «стеком вызовов». Как только поток выполняет свой код, стек вызовов изменяется.
Стек потока содержит все локальные переменные для каждого выполняемого метода. Поток может получить доступ только к своему стеку. Локальные переменные, невидимы для всех других потоков, кроме потока, который их создал. Даже если два потока выполняют один и тот же код, они всё равно будут создавать локальные переменные этого кода в своих собственных стеках. Таким образом, каждый поток имеет свою версию каждой локальной переменной.
Все локальные переменные примитивных типов (boolean, byte, short, char, int, long, float, double) полностью хранятся в стеке потоков и не видны другим потокам. Один поток может передать копию примитивной переменной другому потоку, но не может совместно использовать примитивную локальную переменную.
Куча содержит все объекты, созданные в вашем приложении, независимо от того, какой поток создал объект. К этому относятся и версии объектов примитивных типов (например, Byte, Integer, Long и т.д.). Неважно, был ли объект создан и присвоен локальной переменной или создан как переменная-член другого объекта, он хранится в куче.
Ниже диаграмма, которая иллюстрирует стек вызовов и локальные переменные (они хранятся в стеках), а также объекты (они хранятся в куче):
Локальная переменная может быть примитивного типа, в этом случае она полностью хранится в стеке потока.
Локальная переменная также может быть ссылкой на объект. В этом случае ссылка (локальная переменная) хранится в стеке потоков, но сам объект хранится в куче.
Объект может содержать методы, и эти методы могут содержать локальные переменные. Эти локальные переменные также хранятся в стеке потоков, даже если объект, которому принадлежит метод, хранится в куче.
Переменные-члены объекта хранятся в куче вместе с самим объектом. Это верно как в случае, когда переменная-член имеет примитивный тип, так и в том случае, если она является ссылкой на объект.
Статические переменные класса также хранятся в куче вместе с определением класса.
К объектам в куче могут обращаться все потоки, имеющие ссылку на объект. Когда поток имеет доступ к объекту, он также может получить доступ к переменным-членам этого объекта. Если два потока вызывают метод для одного и того же объекта одновременно, они оба будут иметь доступ к переменным-членам объекта, но каждый поток будет иметь свою собственную копию локальных переменных.
Диаграмма, которая иллюстрирует описанное выше:
Два потока имеют набор локальных переменных. Local Variable 2 указывает на общий объект в куче (Object 3). Каждый из потоков имеет свою копию локальной переменной со своей ссылкой. Их ссылки являются локальными переменными и поэтому хранятся в стеках потоков. Тем не менее, две разные ссылки указывают на один и тот же объект в куче.
Обратите внимание, что общий Object 3 имеет ссылки на Object 2 и Object 4 как переменные-члены (показано стрелками). Через эти ссылки два потока могут получить доступ к Object 2 и Object 4.
На диаграмме также показана локальная переменная (Local variable 1). Каждая её копия содержит разные ссылки, которые указывают на два разных объекта (Object 1 и Object 5), а не на один и тот же. Теоретически оба потока могут обращаться как к Object 1, так и к Object 5, если они имеют ссылки на оба этих объекта. Но на диаграмме выше каждый поток имеет ссылку только на один из двух объектов.
Итак, мы посмотрели иллюстрацию, теперь давайте посмотрим, как тоже самое выглядит в Java-коде:
Метод run() вызывает methodOne(), а methodOne() вызывает methodTwo().
methodOne() объявляет примитивную локальную переменную (localVariable1) типа int и локальную переменную (localVariable2), которая является ссылкой на объект.
Каждый поток, выполняющий методOne(), создаст свою собственную копию localVariable1 и localVariable2 в своих соответствующих стеках. Переменные localVariable1 будут полностью отделены друг от друга, находясь в стеке каждого потока. Один поток не может видеть, какие изменения вносит другой поток в свою копию localVariable1.
Каждый поток, выполняющий методOne(), также создает свою собственную копию localVariable2. Однако две разные копии localVariable2 в конечном итоге указывают на один и тот же объект в куче. Дело в том, что localVariable2 указывает на объект, на который ссылается статическая переменная sharedInstance. Существует только одна копия статической переменной, и эта копия хранится в куче. Таким образом, обе копии localVariable2 в конечном итоге указывают на один и тот же экземпляр MySharedObject. Экземпляр MySharedObject также хранится в куче. Он соответствует Object 3 на диаграмме выше.
Обратите внимание, что класс MySharedObject также содержит две переменные-члены. Сами переменные-члены хранятся в куче вместе с объектом. Две переменные-члены указывают на два других объекта Integer. Эти целочисленные объекты соответствуют Object 2 и Object 4 на диаграмме.
Также обратите внимание, что methodTwo() создает локальную переменную с именем localVariable1. Эта локальная переменная является ссылкой на объект типа Integer. Метод устанавливает ссылку localVariable1 для указания на новый экземпляр Integer. Ссылка будет храниться в своей копии localVariable1 для каждого потока. Два экземпляра Integer будут сохранены в куче и, поскольку метод создает новый объект Integer при каждом выполнении, два потока, выполняющие этот метод, будут создавать отдельные экземпляры Integer. Они соответствуют Object 1 и Object 5 на диаграмме выше.
Обратите также внимание на две переменные-члены в классе MySharedObject типа long, который является примитивным типом. Поскольку эти переменные являются переменными-членами, они все еще хранятся в куче вместе с объектом. В стеке потоков хранятся только локальные переменные.
Управление памятью на Java. Модель памяти Java. Модель памяти JVM. Распределение памяти Java, Молодое поколение, Старое поколение, Постоянное поколение, Куча, Стек.
Понимание Модели памяти JVM , Управления памятью Java очень важно, если вы хотите понять работу Сборки мусора Java . Сегодня мы рассмотрим управление памятью в Java, различные части памяти JVM и то, как отслеживать и выполнять настройку сборки мусора.
Модель памяти Java (JVM)
Как вы можете видеть на приведенном выше изображении, память JVM разделена на отдельные части. На широком уровне память кучи JVM физически разделена на две части – Молодое поколение и Старое поколение .
Управление памятью на Java – Молодое поколение
Молодое поколение-это место, где создаются все новые объекты. Когда молодое поколение заполняется, производится сбор мусора. Эта сборка мусора называется Minor GC . Молодое поколение разделено на три части – Память Эдема и две Память выживших пространства.
Важные моменты, касающиеся пространств для молодого поколения:
- Большинство вновь созданных объектов находятся в пространстве памяти Eden.
- Когда пространство Eden заполняется объектами, выполняется незначительная сборка, и все объекты, оставшиеся в живых, перемещаются в одно из пространств, оставшихся в живых.
- Второстепенный GC также проверяет объекты выживших и перемещает их в другое пространство выживших. Таким образом, за один раз одно из оставшихся в живых мест всегда пустует.
- Объекты, которые сохранились после многих циклов GC, перемещаются в пространство памяти старого поколения. Обычно это делается путем установления порогового значения возраста объектов молодого поколения, прежде чем они получат право на продвижение к старому поколению.
Управление памятью в Java – Старое поколение
Память старого поколения содержит объекты, которые являются долгоживущими и сохранились после многих раундов незначительной GC. Обычно сборка мусора выполняется в памяти старого поколения, когда она заполнена. Сборка мусора старого поколения называется Major GC и обычно занимает больше времени.
Остановите Мировое событие
Поскольку молодое поколение хранит недолговечные объекты, незначительная GC выполняется очень быстро, и это не влияет на приложение.
Однако основная GC занимает много времени, потому что она проверяет все живые объекты. Основные операции GC должны быть сведены к минимуму, поскольку это приведет к тому, что ваше приложение не будет отвечать на запросы во время сбора мусора. Поэтому, если у вас есть отзывчивое приложение и происходит много крупных операций по сбору мусора, вы заметите ошибки тайм-аута.
Продолжительность, затрачиваемая сборщиком мусора, зависит от стратегии, используемой для сбора мусора. Вот почему необходимо отслеживать и настраивать сборщик мусора, чтобы избежать тайм-аутов в приложениях с высокой чувствительностью.
Модель памяти Java – Постоянное поколение
Пермское поколение заполняется JVM во время выполнения на основе классов, используемых приложением. Perm Gen также содержит классы и методы библиотеки Java SE. Объекты пермского поколения-это мусор, собранный в полной сборке мусора.
Модель памяти Java – Область Методов
Область методов является частью пространства в PermGen и используется для хранения структуры классов (констант времени выполнения и статических переменных) и кода для методов и конструкторов.
Модель памяти Java – Пул памяти
Пулы памяти создаются менеджерами памяти JVM для создания пула неизменяемых объектов, если реализация поддерживает это. Пул строк-хороший пример такого пула памяти. Пул памяти может принадлежать куче или PermGen, в зависимости от реализации диспетчера памяти JVM.
Модель памяти Java – Пул констант времени Выполнения
Пул констант времени выполнения-это представление во время выполнения для каждого класса пула констант в классе. Он содержит константы времени выполнения класса и статические методы. Пул констант времени выполнения является частью области методов.
Модель памяти Java – Память стека Java
Память стека Java используется для выполнения потока. Они содержат кратковременные значения для конкретного метода и ссылки на другие объекты в куче, на которые ссылается метод. Вы должны прочитать Разницу между стековой и кучной памятью .
Управление памятью в Java – Переключатели памяти кучи Java
Java предоставляет множество переключателей памяти, которые мы можем использовать для установки размеров памяти и их соотношений. Некоторые из часто используемых переключателей памяти являются:
-Xms | Для установки начального размера кучи при запуске JVM |
-Xmx | Для установки максимального размера кучи. |
-Xmn | Для определения численности молодого поколения остальная часть пространства отводится Старому поколению. |
-XX:ПермГен | Для установки начального размера постоянной памяти поколения |
-XX:MaxPermGen | Для установки максимального размера PermGen |
-XX:Выживание | Для обеспечения соотношения пространства Эдема и пространства Выживших, например, если размер молодого поколения составляет 10 м, а переключатель виртуальной машины равен 5 м, то для пространства Эдема будет зарезервировано 5 м, а для обоих пространств Выживших-по 2,5 м. Значение по умолчанию равно 8. |
-XX:Новое время | Для обеспечения соотношения размеров старого/нового поколения. Значение по умолчанию равно 2. |
В большинстве случаев вышеуказанных опций достаточно, но если вы хотите проверить и другие опции, пожалуйста, проверьте Официальную страницу опций JVM .
Управление памятью в Java – Сборка мусора Java
Сборка мусора Java-это процесс идентификации и удаления неиспользуемых объектов из памяти и свободного пространства, которые будут выделены объектам, созданным в ходе будущей обработки. Одной из лучших особенностей языка программирования Java является автоматическая сборка мусора , в отличие от других языков программирования, таких как C, где выделение и освобождение памяти выполняется вручную.
Один из основных способов сбора мусора включает в себя три этапа:
- Маркировка : Это первый шаг, на котором сборщик мусора определяет, какие объекты используются, а какие нет.
- Обычное удаление : Сборщик мусора удаляет неиспользуемые объекты и освобождает свободное пространство, которое будет выделено другим объектам.
- Удаление с уплотнением : Для повышения производительности после удаления неиспользуемых объектов все уцелевшие объекты можно переместить, чтобы они были вместе. Это повысит производительность выделения памяти более новым объектам.
Есть две проблемы с простым подходом к пометке и удалению.
Вышеуказанные недостатки при простом подходе являются причиной того, что Сборка мусора Java является поколенческой и у нас есть Молодое поколение и Старое поколение места в памяти кучи. Я уже объяснял выше, как объекты сканируются и перемещаются из одного пространства поколений в другое на основе второстепенного GC и Основного GC.
Управление памятью в Java – Типы сборки мусора Java
Существует пять типов сбора мусора, которые мы можем использовать в наших приложениях. Нам просто нужно использовать переключатель JVM, чтобы включить стратегию сбора мусора для приложения. Давайте рассмотрим каждый из них по очереди.
Управление памятью в Java – Мониторинг сборки мусора Java
Мы можем использовать командную строку Java, а также инструменты пользовательского интерфейса для мониторинга действий по сбору мусора в приложении. Для моего примера я использую одно из демонстрационных приложений, предоставляемых загрузками Java SE.
Если вы хотите использовать одно и то же приложение, перейдите на страницу Загрузки Java SE и загрузите Демонстрации и образцы JDK 7 и JavaFX . Пример приложения, которое я использую, это Java2Demo.jar и он присутствует в каталоге jdk1.7.0_55/demo/jfc/Java2D . Однако это необязательный шаг, и вы можете запускать команды мониторинга GC для любого приложения java.
Команда, используемая мной для запуска демонстрационного приложения, является:
jstat
Мы можем использовать jstat инструмент командной строки для мониторинга памяти JVM и операций по сбору мусора. Он поставляется со стандартным JDK, так что вам не нужно ничего делать, чтобы его получить.
Для выполнения jstat вам необходимо знать идентификатор процесса приложения, вы можете легко его получить, используя команду ps-eaf | grep java .
Последним аргументом для jstat является интервал времени между каждым выводом, поэтому он будет печатать данные о памяти и сборке мусора каждые 1 секунду.
Давайте пройдемся по каждой колонке по очереди.
- S0C и S1C : В этом столбце показан текущий размер областей Выжившего 0 и Выжившего 1 в КБ.
- S0 и S1U : В этом столбце показано текущее использование областей Выживший 0 и Выживший 1 в КБ. Обратите внимание, что одна из областей выживших все время пуста.
- ЕС и ЕС : Эти столбцы показывают текущий размер и использование пространства Eden в КБ. Обратите внимание, что размер ЕС увеличивается, и как только он пересекает ЕС, вызывается Второстепенный GC и размер ЕС уменьшается.
- OC и OU : Эти столбцы показывают текущий размер и текущее использование старого поколения в КБ.
- PC и PU : В этих столбцах показан текущий размер и текущее использование PermGen в КБ.
- YGC и YGC : в столбце YGC отображается количество событий GC, произошедших в молодом поколении. В столбце YGCT отображается накопленное время для операций GC для молодого поколения. Обратите внимание, что оба они увеличены в одной строке, где значение EU уменьшается из-за незначительного GC.
- FGC и FGC : в столбце FGC отображается количество произошедших полных событий GC. В столбце FGCT отображается накопленное время для полных операций GC. Обратите внимание, что полное время GC слишком велико по сравнению с временем GC молодого поколения.
- GCT : В этом столбце отображается общее накопленное время для операций GC. Обратите внимание, что это сумма значений столбцов GCT и GCT.
Преимущество jstat в том, что он также может быть выполнен на удаленных серверах, где у нас нет графического интерфейса. Обратите внимание, что сумма S0C, S1C и EC составляет 10 м, как указано в параметре -Xmn10m JVM.
Java VisualVM с визуальным GC
Если вы хотите видеть операции с памятью и GC в графическом интерфейсе, вы можете использовать jvisualvm инструмент. Java VisualVM также является частью JDK, поэтому вам не нужно загружать его отдельно.
Просто запустите команду jvisualvm в терминале, чтобы запустить приложение Java VisualVM. После запуска вам необходимо установить Visual GC плагин с помощью опции Tools -< Плагины, как показано на рисунке ниже.
После установки Visual GC просто откройте приложение в левой боковой колонке и перейдите в раздел Visual GC . Вы получите изображение памяти JVM и сведения о сборке мусора, как показано на рисунке ниже.
Настройка Сборки мусора Java
Настройка сборки мусора Java должна быть последней опцией, которую вы должны использовать для увеличения пропускной способности вашего приложения, и только тогда, когда вы видите снижение производительности из-за более длительных периодов времени GC, вызывающих тайм-аут приложения.
Если вы видите java.lang.OutOfMemoryError: пространство PermGen ошибки в журналах, затем попробуйте отслеживать и увеличивать пространство памяти PermGen, используя параметры-XX:PermGen и -XX:MaxPermGen JVM. Вы также можете попробовать использовать -XX:+CMSClassUnloadingEnabled и проверить, как он работает с сборщиком мусора CMS.
Если вы видите много полных операций GC, то вам следует попробовать увеличить объем памяти старого поколения.
Общая настройка сборки мусора требует много усилий и времени, и для этого нет жестких и быстрых правил. Вам нужно будет попробовать различные варианты и сравнить их, чтобы найти лучший, подходящий для вашего приложения.
Это все для модели памяти Java, управления памятью в Java и сбора мусора, я надеюсь, что это поможет вам понять память JVM и процесс сбора мусора.
У каждого компьютера есть оперативная память . Что же это такое, какими свойствами обладает и, самое главное, какая нам от этого польза?
Каждая программа (в том числе и программы , написанные на Java) перед выполнением загружается в оперативную память . В оперативной памяти находится код программы (который исполняется процессором) и данные программы (которые в память помещает сама программа).
Что же такое оперативная память и на что она похожа?
Процессор умеет исполнять команды из загруженной в память программы. Почти все команды процессора — это что-то типа: взять данные из некоторых ячеек → сделать с ними что-то → результат поместить в другие ячейки
Объединяя сотни простых команд, мы получаем сложные и полезные команды.
Когда в коде программы объявляется переменная, ей выделяется кусочек ещё не использованной памяти . Обычно это несколько байт. При объявлении переменной обязательно нужно указать тип информации, которую программа будет хранить в ней: числа, текст, или другие данные. Ведь не зная тип информации, не ясно, какого размера блок памяти нужно выделить под переменную.
На заре компьютерной отрасли программы работали просто с номерами ячеек памяти, но потом для удобства программистов ячейкам стали давать имена. Уникальное имя переменной — это в первую очередь для удобства программистов: программа во время работы отлично справилась бы и с номерами.
2. Переменные в памяти
Всего в Java есть 4 типа данных для хранения целых чисел. Это byte , short , int и long .
Тип | Размер, байт | Происхождение имени |
---|---|---|
byte | 1 | Byte , т.к. занимает один байт памяти |
short | 2 | Сокращение от Short Integer |
int | 4 | Сокращение от Integer |
long | 8 | Сокращение от Long Integer |
Также в Java есть 2 вещественных типа — float и double:
Тип | Размер, байт | Происхождение имени |
---|---|---|
float | 4 | Сокращение от Floating Point Number |
double | 8 | Сокращение от Double Float |
Каждый раз, когда выполнение программы доходит до команды создания переменной, ей выделяется небольшая область памяти (размер зависит от типа переменной).
Адресом переменной считается адрес первой ячейки выделенного под нее блока памяти.Java-программам запрещено напрямую обращаться к памяти. Вся работа с памятью происходит только через Java-машину.
3. Тип String в памяти
Тип String может хранить большие объемы данных, поэтому это не просто тип данных, а полноценный класс.
Сами данные типа String (текст) помещаются в специальный объект, под который выделяется память, а уже адрес этого объекта помещается в переменную, под которую тоже выделяется память.
Переменная a типа int занимает 4 байта и хранит значение 1 .
Переменная b типа int занимает 4 байта и хранит значение 10,555 . Запятая - это не дробная часть числа, а разделение разрядов. Дробная часть отделяется точкой
Переменная d типа double занимает 8 байт и хранит значение 13.001 .
Переменная str типа String занимает 4 байта и хранит значение G13 — адрес первой ячейки объекта, содержащего текст.
Объект типа String (содержащий текст) хранится отдельным блоком памяти. Адрес его первой ячейки хранится в переменной str .
4. Почему в программировании всё нумеруют с нуля
Люди очень часто удивляются, почему в программировании почти везде принято считать с нуля. Дело в том, что есть очень много ситуаций, когда считать с нуля удобнее (хотя есть и ситуации, когда удобнее считать с 1 ).
Когда мы думаем об относительном адресе внутри какого-либо блока данных, всегда получаем нумерацию с нуля. Это и есть первая и самая распространенная причина счета с нуля .
Читайте также: