Важной особенностью Java является автоматическое управление переработкой памяти через коллектор мусора (GC), не требуя от программистов самостоятельно освобождать память. Теоретически, вся память, занятая объектами, которые больше не будут использоваться на Java, может быть переработана GC, но у Java также есть утечки памяти, но ее производительность отличается от C ++.
Управление памятью на Java
Чтобы понять утечки памяти в Java, вы должны сначала знать, как управляет память в Java.
В программах Java мы обычно используем новое для распределения памяти на объекты, и эти пространства памяти находятся на куче (куча).
Вот пример:
открытый класс простой {public static void main (string args []) {object object1 = new object (); // obj1 object2 = new object (); // obj2 object2 = object1; //.... На этот раз, OBJ2 может быть очищен}}Java использует направленные графики для управления памятью:
На направленном графике мы называем OBJ1 быть доступным, OBJ2 быть недоступным, и, очевидно, недоступно может быть очищен.
Выпуск памяти, то есть очистка недоступных объектов, определяется и выполняется GC, поэтому GC будет отслеживать состояние каждого объекта, включая применение, цитирование, цитирование и назначение. Фундаментальный принцип освобождения объекта заключается в том, что объект больше не будет использоваться:
Объекту дается нулевое значение, и его больше никогда не вызывали.
Другой - присвоить новое значение объекту, который перераспределяет пространство памяти.
Обычно считается, что стоимость выделения объектов на кучу относительно высока, но GC оптимизирует эту операцию: в C ++ распределение куска памяти на куче будет искать кусок соответствующей памяти для распределения. Если объект уничтожен, этот кусок памяти может быть использован повторно; В Java вы хотите длинную полосу. Каждый раз, когда выделяется новый объект, Java «Pointer» перемещается назад в область, которая не была выделена. Следовательно, эффективность распределяющей памяти Java сопоставима с эффективностью C ++.
Но есть проблема с таким способом работы: если память часто применяется, ресурсы будут исчерпываны. В настоящее время GC вмешивается, он восстанавливает пространство и делает объекты в куче более компактными. Таким образом, всегда будет достаточно места памяти, чтобы распределить.
Метод счета ссылки при очистке GC: когда ссылка подключена к новому объекту, количество ссылок составляет +1; Когда ссылка покидает прицел или устанавливается на NULL, количество эталонов составляет -1. Когда GC обнаруживает, что этот счет равен 0, он перерабатывает память, которую он потребляет. Эта накладная часть происходит на протяжении всего срока службы справочной программы и не может обрабатывать круговые ссылки. Таким образом, этот метод просто используется для иллюстрации того, как работает GC, и не будет применен ни одной виртуальной машиной Java.
Большинство GC принимают метод адаптивной очистки (плюс другие дополнительные методы для повышения скорости), в основном на поиске любых «живых» объектов, а затем с использованием «адаптивного, поколений, маркировного координатора мусора». Я не буду вводить слишком много, это не в центре внимания этой статьи.
Утечка памяти на Java
Утечка памяти в Java, в целом и в терминах непрофессионала, заключается в том, что память о объектах, которые больше не будут использоваться, не может быть переработана, что является утечкой памяти.
Утечки памяти в Java отличаются от утечек в C ++.
В C ++ все объекты, которые были выделены памятью, должны быть вручную выпущены программистом после того, как они больше не используются. Следовательно, каждый класс будет содержать деструктор, который должен завершить работу по очистке. Если мы забудем выпустить определенные объекты, это вызовет утечку памяти.
Но в Java нам не нужно (и не можем) освобождать память сами, и бесполезные объекты автоматически очищаются GC, что значительно упрощает нашу работу по программированию. Однако иногда некоторые объекты, которые больше не будут использоваться, не могут быть выпущены в GC, что вызовет утечку памяти.
Мы знаем, что объекты имеют жизненные циклы, некоторые длинные, а некоторые короткие. Если объекты длительного жизненного цикла содержат ссылки с короткими жизненными циклами, вероятно, утечка памяти. Давайте приведем простой пример:
открытый класс простой {объект объекта; public void method1 () {object = new Object (); //.....Other Code}}На самом деле, мы ожидаем, что он будет использоваться только в методе Method1 (), и он не будет использоваться в другом месте. Однако, когда метод Method1 () выполняется, память, выделенная объектом объекта, не будет сразу же считаться объектом, который может быть опубликован, и будет выпущен только после того, как объект, созданный Simple Class, будет выпущен. Строго говоря, это утечка памяти. Решение состоит в том, чтобы использовать объект в качестве локальной переменной в методе Method1 (). Конечно, если вам нужно это написать, вы можете изменить это на это:
открытый класс простой {объект объекта; public void method1 () {object = new Object (); //.....Other Code Object = null; }}Таким образом, память, выделенная «NewObject ()», может быть переработана GC.
На этом этапе утечки памяти Java должны быть более ясными. Давайте объясним далее ниже:
Когда выделенная память в куче не выпускается, все способы доступа к этой памяти удаляются (например, переназначение указателя). Это для таких языков, как C ++. GC в Java поможет нам справиться с этой ситуацией, поэтому нам не нужно заботиться.
Когда объект памяти, очевидно, не нужен, он все еще сохраняет эту память и метод доступа (ссылка), который является утечкой памяти, которая может возникнуть на всех языках. Если вы не осторожны при программировании, это легко произойти, и если это не слишком серьезно, это может быть просто короткая утечка памяти.
Некоторые примеры и решения, которые склонны к утечке памяти
Ситуация, подобная вышеуказанному примеру, легко произойти, и это также наиболее вероятной ситуации, которую мы игнорируем и вызывают утечки памяти. Решение состоит в том, чтобы свести к минимуму объем объекта (например, в AndroidStudio, приведенный выше код выпустит предупреждение, и приведенные предложения заключаются в переписывании переменных элементов класса в локальные переменные в методе) и вручную установить нулевое значение.
Что касается масштаба, мы должны уделять больше внимания при написании кода; Настройка нулевого значения, мы можем взглянуть на внутренний метод удаления указанного узла в исходном коде Java Container LinkedList (см . Св.
// Удалить указанный узел и вернуть значение удаленного элемента e Unlink (node <e> x) {// Получить текущее значение и передние и задние узлы Final E element = x.item; Окончательный узел <e> next = x.next; Окончательный узел <e> prev = x.prev; if (prev == null) {First = Next; // Если предыдущий узел пуст (например, текущий узел является первым узлом), следующий узел становится новым первым узлом} else {prev.next = Next; // Если предыдущий узел не является пустым, то он указывает на текущий следующий узел X.Prev = null; } if (next == null) {last = prev; // Если следующий узел пуст (например, текущий узел является хвостовым узлом), предыдущий из текущего узла становится новым хвостовым узлом} else {next.prev = prev; // Если следующий узел не является пустым, следующий узел направлен на то, что следующий узел указывает на текущий предыдущий узел X.Next = null; } x.item = null; размер--; modcount ++; возвратный элемент; }В дополнение к изменению взаимосвязи между узлами, что нам также нужно сделать, это присвоить значение NULL. Независимо от того, когда GC начнет чистку, мы должны отмечать бесполезные объекты как чистящие объекты во времени.
Мы знаем, что Java Container ArrayList реализован в массиве (см.: См. Использование исходного кода Java ArrayList (JDK1.8) ). Если мы хотим написать для него метод POP () (POP), он может выглядеть так:
public e pop () {if (size == 0) return null; иначе вернуть (e) elementdata [-size]; }Метод написания очень краткий, но он приведет к переполнению памяти здесь: ElementData [Size-1] по-прежнему содержит ссылку на объект E-Type и не может быть переработана GC в течение времени. Мы можем изменить его следующим образом:
public e pop () {if (size == 0) return null; else {e e = (e) elementdata [-size]; elementData [size] = null; вернуть E; }}При написании кода мы не можем слепо преследовать простоту. Первое, что обеспечивает его точность.
Утечки памяти во время использования контейнера
Во многих статьях вы можете увидеть пример утечек памяти следующим образом:
Вектор v = новый вектор (); for (int i = 1; i <100; i ++) {object o = new Object (); v.add (o); o = null; }Многие люди могут не понимать этого в начале, поэтому мы можем понять приведенный выше код следующим образом:
void method () {Vector Vector = new Vector (); for (int i = 1; i <100; i ++) {object object = new Object (); Vector.Add (Object); Object = null; } //...Operations on Vector // ... другие операции, которые не связаны с вектором}Здесь утечки в памяти относятся к тому факту, что после завершения векторной операции, если выполняется операция GC, эта серия объектов не может быть переработана. Утечка памяти здесь может быть недолгой, потому что после выполнения всего метода метода () эти объекты все еще могут быть переработаны. Здесь очень просто решить, просто вручную назначить значение NULL:
void method () {Vector Vector = new Vector (); for (int i = 1; i <100; i ++) {object object = new Object (); Vector.Add (Object); Object = null; } //...Operations on v Vector = null; //... Другие операции, которые не связаны с V}Приведенный выше вектор устарел, но это просто введение в утечки памяти, используя старые примеры. Когда мы используем контейнеры, легко вызвать утечку памяти, как приведенный выше пример. Однако в приведенном выше примере эффект утечки памяти, вызванный локальными переменными в методе во время контейнера, может быть не очень большим (но мы также должны избежать его). Однако, если этот контейнер является переменной члена класса или даже статической переменной члена, вы должны уделять больше внимания утечке памяти.
Ниже приведена ошибка, которая может возникнуть при использовании контейнеров:
открытый класс CollectionMemory {public static void main (string s []) {set <MyObject> objects = new LinkedHashset <yobject> (); Objects.Add (новый myObject ()); Objects.Add (новый myObject ()); Objects.Add (новый myObject ()); Objects.Add (новый myObject ()); System.out.println (objects.size ()); while (true) {objects.add (new myobject ()); }}} класс myObject {// Установить длину массива по умолчанию на 99999 и происходит быстрее outofmemororror list <string> list = new ArrayList <> (99999);}Запуск вышеупомянутого кода очень быстро сообщит об ошибке:
3 Exception в потоке "Main" java.lang.outofmemoryerror: Java Heap Space at java.util.arraylist. com.anxpp.memory.collectionmemory.main (CollectionMemory.java:16)
Если вы знаете достаточно о контейнерах Java, вышеуказанная ошибка не произойдет. Вот также пост, который я представляю Java Containers: ...
Набор контейнеров хранит только уникальные элементы и сравнивается с помощью метода Equals (). Однако все классы в Java прямо или косвенно унаследованы в классе объекта. Метод equals () класса объекта сравнивает адрес объекта. В приведенном выше примере элементы будут добавлены до переполнения памяти.
Следовательно, приведенный выше пример - строго говоря, переполнение памяти, вызванное неправильным использованием контейнера.
Что касается установки, метод remove () также использует метод evel () для удаления соответствующих элементов. Если объект действительно обеспечивает правильный метод Equals (), помните, что не используйте remove (ObjectO) после изменения этого объекта, это также может вызвать утечку памяти.
Различные объекты, которые обеспечивают метод Close ()
Например, при использовании подключений базы данных (dataSury.getConnection ()), сетевых соединений (сокетов) и IO подключений, а при использовании других структур они не будут автоматически переработаны с помощью GC, если только они явно вызывают свой метод Close (или аналогичный метод), чтобы закрыть свое соединение. Фактически, причина в том, что объекты с длительным циклом содержат ссылки на объекты с коротким жизненным циклом.
Многие люди могли использовать Hibernate. Когда мы работаем в базе данных, мы получаем сеанс через SessionFactory:
Session Session = sessionFactory.Opensession ();
После завершения мы должны вызвать метод Close () для закрытия:
session.close ();
SessionFactory-это долгосрочный объект, а сеанс является относительно коротким объектом, но структура разработана таким образом: он не знает, как долго мы будем использовать сеанс, поэтому она может предоставить нам только способ решать, когда мы больше не будем его использовать.
Поскольку исключение может быть выброшено до того, как будет вызван метод Close (), поэтому метод не может быть вызван. Обычно используем язык TRY, а затем, наконец, выполняем Close () и другие работы по очистке в операторе:
try {session = sessionfactory.opensession (); //.... Другие операции} наконец {session.close (); }Утечки памяти, вызванные режимом синглтона
Во многих случаях мы можем рассматривать его жизненный цикл как похожий на жизненный цикл всей программы, так что это объект с долгим жизненным циклом. Если этот объект содержит ссылки на другие объекты, утечки памяти также склонны.
Ссылки на внутренние классы и внешние модули
На самом деле, принцип остается таким же, но то, как он кажется другим.
Методы, связанные с очисткой
В этом разделе в основном рассказывается о методах GC () и завершения ().
GC ()
Для программистов GC в основном прозрачен и невидим. Функция, которая запускает GC System.gc (), и после вызова ее запускает сборщик мусора и начинает чистить.
Однако, согласно определению спецификации языка Java, эта функция не гарантирует, что коллекционер мусора JVM выполнит. Потому что разные реализователи JVM могут использовать разные алгоритмы для управления GC. Как правило, потоки GC имеют более низкий приоритет.
Есть много стратегий для JVM, чтобы вызвать GC. Некоторые из них начинают работать только тогда, когда использование памяти достигает определенного уровня. Некоторые выполняют их регулярно. Некоторые выполняют GC плавно, а некоторые выполняют GC в манере прерывания. Но, вообще говоря, нам не нужно заботиться об этом. Если в некоторых конкретных ситуациях выполнение GC влияет на производительность приложения. Например, для веб-систем в реальном времени, таких как онлайн-игры, пользователи не хотят, чтобы GC внезапно прерывал выполнение приложений и выполняла сбор мусора, тогда нам необходимо скорректировать параметры GC, чтобы GC мог свободно воспроизводить память, такую как разложение сбора мусора в серию небольших шагов для выполнения. Hotspotjvm, предоставленная Sun, поддерживает эту функцию.
Завершите ()
infintize () - это метод в классе объекта.
Те, кто знает C ++, знают, что существует деструктор, но обратите внимание, что Pinlineize () ни в коем случае не равен деструктору в C ++.
Это объясняется в идее программирования Java: как только GC будет готов освободить пространство для хранения, занятое объектом, его метод завершения () будет называться первым, а память, занятая объектом, будет переработана только тогда, когда произойдет следующее действие по переработке GC, поэтому мы можем поместить некоторые работы по очистке в Finalize ().
Важной целью этого метода является: при вызове не-Java-кода (например, C и C ++) в Java, соответствующие операции с применением памяти (такие как функция C Malloc ()) могут использоваться в этих не Java-кодах, и в этих не Java-кодах эти память не высвобождают эту память эффективно, вы можете использовать метод окончательного () и вызовать локальные методы () и другие функции.
Поэтому Finalize () не подходит для обычной очистки.
Однако иногда этот метод имеет некоторые применения:
Если есть серия объектов, один из объектов имеет состояние false. Если мы обработали этот объект, государство станет правдой. Чтобы избежать пропущенных объектов без обработки, вы можете использовать метод infintize ():
класс myObject {логическое состояние = false; public void deal () {//... некоторые операции обработки state = true; } @Override Protected void infintize () {if (! State) {System.out.println ("error:" + "объект не обрабатывается!"); }} // ...}Но из многих аспектов рекомендуется не использовать и считаться избыточным.
В целом, утечки памяти вызваны плохим кодированием. Мы не можем винить JVM в том, что они не убирают более разумно.
Суммировать
Выше приведено все подробное объяснение просочившегося кода памяти на языке Java. Я надеюсь, что это будет полезно для всех. Заинтересованные друзья могут продолжать ссылаться на другие связанные темы на этом сайте. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте!