memory что - Создание утечки памяти с помощью Java




такое памятью (25)

Вероятно, одним из простейших примеров потенциальной утечки памяти, и как ее избежать, является реализация ArrayList.remove (int):

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    elementData[--size] = null; // (!) Let gc do its work

    return oldValue;
}

Если бы вы сами его реализовали, подумали бы вы очистить элемент массива, который больше не используется ( elementData[--size] = null )? Эта ссылка может сохранить живой объект ...

У меня просто было интервью, и меня попросили создать утечку памяти с помощью Java. Излишне говорить, что я чувствовал себя довольно глупым, не имея понятия о том, как начать создавать его.

Какой пример?


Создайте статическую карту и продолжайте добавлять к ней жесткие ссылки. Те никогда не будут GC'd.

public class Leaker {
    private static final Map<String, Object> CACHE = new HashMap<String, Object>();

    // Keep adding until failure.
    public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}

Я недавно столкнулся с ситуацией утечки памяти, вызванной способом log4j.

Log4j имеет этот механизм, называемый вложенным диагностическим контекстом (NDC), который является инструментом для различения данных с чередованием журналов из разных источников. Гранулярность, с которой работает NDC, - это потоки, поэтому она различает выходные данные из разных потоков отдельно.

Чтобы хранить теги, специфичные для потока, класс NDC log4j использует Hashtable, который связан с самим объектом Thread (в отличие от идентификатора потока), и, таким образом, до тех пор, пока тег NDC не останется в памяти всех объектов, зависающих от потока объект также остается в памяти. В нашем веб-приложении мы используем NDC для тегов logoutputs с идентификатором запроса, чтобы отличать журналы от одного запроса отдельно. Контейнер, который связывает тег NDC с потоком, также удаляет его, возвращая ответ от запроса. Проблема возникла, когда во время обработки запроса был создан дочерний поток, что-то вроде следующего кода:

pubclic class RequestProcessor {
    private static final Logger logger = Logger.getLogger(RequestProcessor.class);
    public void doSomething()  {
        ....
        final List<String> hugeList = new ArrayList<String>(10000);
        new Thread() {
           public void run() {
               logger.info("Child thread spawned")
               for(String s:hugeList) {
                   ....
               }
           }
        }.start();
    }
}    

Таким образом, контекст NDC был связан с встроенным потоком, который был порожден. Объект потока, который был ключом для этого контекста NDC, является встроенным потоком, который имеет огромный объект LIST, зависающий от него. Следовательно, даже после того, как поток закончил делать то, что он делал, ссылка на огромный список была поддержана контекстом NDC Hastable, что вызвало утечку памяти.


Каждый раз, когда вы держите ссылки на объекты, которые вам больше не нужны, у вас есть утечка памяти. См. Раздел Обработка утечек памяти в Java-программах для примеров того, как утечка памяти проявляется на Java и что вы можете с ней поделать.


Нитки не собираются до тех пор, пока они не прекратятся. Они служат roots сбора мусора. Они являются одним из немногих объектов, которые не будут восстановлены, просто забыв о них или очистив ссылки на них.

Рассмотрим: основной шаблон для завершения рабочего потока - это установить некоторую переменную условия, видимую потоком. Поток может периодически проверять переменную и использовать это как сигнал для завершения. Если переменная не объявлена volatile, изменение в переменной может не отображаться нитью, поэтому она не будет знать, что она прекратится. Или представьте, если некоторые потоки хотят обновить общий объект, но тупик при попытке заблокировать его.

Если у вас есть только несколько потоков, эти ошибки, вероятно, будут очевидны, потому что ваша программа перестанет работать должным образом. Если у вас есть пул потоков, который при необходимости создает больше потоков, то устаревшие / застрявшие потоки могут быть не замечены и будут накапливаться бесконечно, вызывая утечку памяти. Потоки, скорее всего, будут использовать другие данные в вашем приложении, а также предотвратят все, что они непосредственно ссылаются, из когда-либо собранных.

В качестве примера игрушек:

static void leakMe(final Object object) {
    new Thread() {
        public void run() {
            Object o = object;
            for (;;) {
                try {
                    sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {}
            }
        }
    }.start();
}

Вызовите System.gc()все, что вам нравится, но переданный объект leakMeникогда не умрет.

(* Изм *)


Каждый всегда забывает маршрут собственного кода. Вот простая формула для утечки:

  1. Объявить собственный метод.
  2. В собственном методе вызовите malloc. Не звоните free.
  3. Вызовите метод native.

Помните, что выделение памяти в собственном коде происходит из кучи JVM.


Возможно, интервьюер искал круглое справочное решение:

    public static void main(String[] args) {
        while (true) {
            Element first = new Element();
            first.next = new Element();
            first.next.next = first;
        }
    }

Это классическая проблема со ссылкой на сборщики мусора. Затем вы вежливо объясните, что JVM используют гораздо более сложный алгоритм, который не имеет этого ограничения.

-Мы Тарле


Следующий пример довольно бессмысленный, если вы не понимаете JDBC . Или, по крайней мере, как JDBC ожидает, что разработчик закрывает экземпляры Connection , Statement и ResultSet перед тем, как отменить их или потерять ссылки на них, вместо того, чтобы полагаться на реализацию finalize .

void doWork()
{
   try
   {
       Connection conn = ConnectionFactory.getConnection();
       PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
       ResultSet rs = stmt.executeQuery();
       while(rs.hasNext())
       {
          ... process the result set
       }
   }
   catch(SQLException sqlEx)
   {
       log(sqlEx);
   }
}

Проблема с вышеизложенным заключается в том, что объект Connection не закрыт, и, следовательно, физическое соединение останется открытым до тех пор, пока сборщик мусора не появится и не увидит, что он недоступен. GC вызовет метод finalize , но есть JDBC-драйверы, которые не реализуют finalize , по крайней мере, не так, как реализовано Connection.close . В результате получается, что, хотя память будет восстановлена ​​из-за сбоя недоступных объектов, ресурсы (включая память), связанные с объектом Connection могут просто не быть восстановлены.

В таком случае, когда метод finalize Connection не очищает все, можно было бы фактически обнаружить, что физическое соединение с сервером базы данных будет длиться несколько циклов сбора мусора, пока сервер базы данных в конце концов не выяснит, что соединение не является живым ( если это так) и должен быть закрыт.

Даже если драйвер JDBC должен был выполнить finalize , во время финализации можно исключить исключения. Полученное поведение состоит в том, что любая память, связанная с теперь «бездействующим» объектом, не будет исправлена, поскольку finalize гарантированно будет вызываться только один раз.

Вышеупомянутый сценарий столкновения с исключениями во время завершения объекта связан с другим другим сценарием, который может привести к воскрешению объекта утечки памяти. Воскрешение объекта часто делается намеренно, создавая сильную ссылку на объект, который должен быть завершен, от другого объекта. Когда воскрешение объекта используется неправильно, это приведет к утечке памяти в сочетании с другими источниками утечек памяти.

Есть еще много примеров, которые вы можете вызвать

  • Управление экземпляром List котором вы добавляете только список и не удаляете его (хотя вам нужно избавиться от ненужных элементов) или
  • Открытие Socket s или File s, но не закрытие их, когда они больше не нужны (аналогично приведенному выше примеру, включающему класс Connection ).
  • Не выгружайте синглеты при подаче приложения Java EE. По-видимому, Classloader, загрузивший singleton-класс, сохранит ссылку на класс, и, следовательно, экземпляр singleton никогда не будет собран. Когда развертывается новый экземпляр приложения, обычно создается новый загрузчик классов, и прежний загрузчик классов будет продолжать существовать из-за синглтона.

Может быть, используя внешний собственный код через JNI?

С чистой Java это почти невозможно.

Но это касается «стандартного» типа утечки памяти, когда вы больше не можете получить доступ к памяти, но она по-прежнему принадлежит приложению. Вместо этого вы можете хранить ссылки на неиспользуемые объекты или открывать потоки, не закрывая их впоследствии.


Я могу скопировать мой ответ отсюда: Самый простой способ вызвать утечку памяти в Java?

«Утечка памяти в компьютерной науке (или утечке в этом контексте) возникает, когда компьютерная программа потребляет память, но не может отпустить ее обратно в операционную систему». (Википедия)

Легкий ответ: вы не можете. Java выполняет автоматическое управление памятью и освобождает ресурсы, которые вам не нужны. Вы не можете остановить это. Он ВСЕГДА сможет освободить ресурсы. В программах с ручным управлением памятью это другое. Вы не можете получить некоторую память в C, используя malloc (). Чтобы освободить память, вам понадобится указатель, возвращаемый malloc, и вызов на него free (). Но если у вас больше нет указателя (перезаписан или превышен срок службы), вы, к сожалению, не сможете освободить эту память, и, следовательно, у вас есть утечка памяти.

Все остальные ответы до сих пор в моем определении не являются утечкой памяти. Все они стремятся наполнить память бессмысленными вещами очень быстро. Но в любое время вы все равно можете разыменовать созданные вами объекты и освободить память -> NO LEAK. Ответ acconrad довольно близок, хотя, как я должен признать, поскольку его решение эффективно просто «разбивает» сборщик мусора, заставляя его в бесконечной петле).

Долгий ответ: вы можете получить утечку памяти, написав библиотеку для Java с помощью JNI, которая может иметь ручное управление памятью и, следовательно, иметь утечки памяти. Если вы вызовете эту библиотеку, ваш java-процесс будет утечка памяти. Или у вас могут быть ошибки в JVM, так что JVM теряет память.Вероятно, в JVM есть ошибки, возможно, даже некоторые из них известны, поскольку сборка мусора не такая тривиальная, но тогда она все еще является ошибкой. По дизайну это невозможно. Возможно, вы запрашиваете какой-то Java-код, который возникает из-за такой ошибки. Извините, я не знаю одного, и в любом случае это может не быть ошибкой в ​​следующей версии Java.


Как и многие люди, утечки ресурсов довольно легко вызвать - например, примеры JDBC. Фактические утечки памяти немного сложнее - особенно если вы не полагаетесь на сломанные биты JVM, чтобы сделать это для вас ...

Идеи создания объектов, которые имеют очень большой след, а затем не могут получить к ним доступ, также не являются настоящими утечками памяти. Если ничто не сможет получить к нему доступ, тогда будет собрано мусор, и если что-то сможет получить к нему доступ, тогда это не утечка ...

Один из способов , который используется для работы , хотя - и я не знаю , если он все еще делает - это иметь три-глубокую круглую цепь. Так как в объекте А имеется ссылка на объект B, объект B имеет ссылку на объект C, а объект C имеет ссылку на объект A. GC был достаточно умен, чтобы знать, что две глубокие цепи - как в A <-> B - можно безопасно собрать, если A и B недоступны ничем другим, но не могут обрабатывать трехстороннюю цепочку ...


Ниже будет неочевидный случай, когда Java утечки, помимо стандартного случая забытых слушателей, статические ссылки, поддельные / модифицируемые ключи в хэшмапах или просто потоки, застрявшие без каких-либо шансов положить конец жизненному циклу.

  • File.deleteOnExit() - всегда File.deleteOnExit() строку, если строка является подстрокой, утечка еще хуже (основной символ [] также просочился) - в подстроке Java 7 также копируется char[] , поэтому более поздняя версия не применяется ; @ Даниэль, не нужны голоса.

Я сосредоточусь на потоках, чтобы показать опасность неуправляемых потоков в основном, не хочу даже прикасаться к качелям.

  • Runtime.addShutdownHook и не удалять ... а затем даже с removeShutdownHook из-за ошибки в классе ThreadGroup относительно неартированных потоков он может не собираться, эффективно утечка ThreadGroup. У JGroup есть утечка в GossipRouter.

  • Создавая, но не начиная, Thread переходит в ту же категорию, что и выше.

  • Создание потока наследует ContextClassLoader и AccessControlContext , а также ThreadGroup и любой InheritedThreadLocal , все эти ссылки являются потенциальными утечками, а также целыми классами, загружаемыми загрузчиком классов и всеми статическими ссылками, и ja-ja. Эффект особенно заметен со всей инфраструктурой jucExecutor, которая содержит супер простой интерфейс ThreadFactory , но большинство разработчиков не имеют понятия скрытой опасности. Также многие библиотеки запускают потоки по запросу (слишком много популярных отраслевых библиотек).

  • ThreadLocal ; во многих случаях это зло. Я уверен, что все видели довольно много простых кешей на основе ThreadLocal, а также плохие новости: если поток продолжает больше, чем ожидалось, жизнь в контексте ClassLoader, это чистая приятная небольшая утечка. Не используйте кэши ThreadLocal, если это действительно необходимо.

  • Вызов ThreadGroup.destroy() когда ThreadGroup не имеет потоков, но он по-прежнему сохраняет дочерние потоковые группы. Плохая утечка, которая предотвратит удаление ThreadGroup из родительского элемента, но все дети становятся un-enumerateable.

  • Использование WeakHashMap и значение (in) напрямую ссылаются на ключ. Это трудно найти без кучи кучи. Это относится ко всем расширенным значениям Weak/SoftReference которые могут содержать твердую ссылку обратно на охраняемый объект.

  • Используя java.net.URL с протоколом HTTP (S) и загрузите ресурс из (!). Этот особенный, KeepAliveCache создает новый поток в системе ThreadGroup, который утечки текущего загрузчика классов потока. Поток создается при первом запросе, когда нет ни одного живого потока, так что вам может повезти или просто утечка. Утечка уже исправлена ​​в Java 7, и код, создающий поток, корректно удаляет загрузчик классов. Есть еще несколько случаев ( как ImageFetcher , также фиксированный ) создания похожих потоков.

  • Использование InflaterInputStream new java.util.zip.Inflater() в конструкторе (например, PNGImageDecoder ) и не вызывает end() надувателя. Ну, если вы передадите конструктору только new , никаких шансов ... И да, вызов close() в потоке не закрывает надув, если он передается вручную как параметр конструктора. Это не настоящая утечка, так как она будет выпущена финализатором ... когда она сочтет это необходимым. До того момента, когда он так сильно поедает родную память, он может заставить Linux oom_killer безнаказанно убить процесс. Основная проблема заключается в том, что финализация на Java очень ненадежна, а G1 ухудшилась до 7.0.2. Мораль истории: как можно скорее выпустите родные ресурсы; финализатор слишком беден.

  • В том же случае с java.util.zip.Deflater . Это намного хуже, так как Deflater является голодной памятью на Java, то есть всегда использует 15 бит (максимум) и 8 уровней памяти (максимум 9), выделяя несколько сотен КБ встроенной памяти. К счастью, Deflater широко не используется, и, насколько мне известно, JDK не содержит злоупотреблений. Всегда вызывайте end() если вы вручную создаете Deflater или Inflater . Лучшая часть последних двух: вы не можете найти их через обычные инструменты для профилирования.

(Я могу добавить еще несколько отрывков времени, с которыми я столкнулся по запросу.)

Удачи и оставайтесь в безопасности; утечки злые!


Я не думаю, что кто-то еще это сказал: вы можете воскресить объект, переопределив метод finalize () таким образом, чтобы finalize () хранит ссылку на это где-то. Сборщик мусора будет вызываться только один раз на объекте, после чего объект никогда не будет уничтожен.


Вот простой / зловещий через http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29 .

public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

Поскольку подстрока относится к внутреннему представлению оригинальной, намного длинной строки, оригинал остается в памяти. Таким образом, если у вас есть StringLeaker в игре, у вас есть целая оригинальная строка в памяти, даже если вы можете подумать, что вы просто держитесь за односимвольную строку.

Способ избежать нежелательной ссылки на исходную строку состоит в том, чтобы сделать что-то вроде этого:

...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

Для дополнительной ошибки вы также можете .intern()использовать подстроку:

...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

Это позволит сохранить как исходную длинную строку, так и производную подстроку в памяти даже после того, как экземпляр StringLeaker был отброшен.


Простая задача - использовать HashSet с неправильным (или несуществующим) hashCode() или equals() , а затем продолжать добавлять «дубликаты». Вместо того, чтобы игнорировать дубликаты, как и следовало ожидать, набор будет только расти, и вы не сможете их удалить.

Если вы хотите, чтобы эти плохие ключи / элементы зависали, вы можете использовать статическое поле, например

class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.

Возьмите любое веб-приложение, работающее в любом контейнере сервлетов (Tomcat, Jetty, Glassfish, что угодно ...). Обновите приложение 10 или 20 раз подряд (этого может быть достаточно, чтобы просто коснуться WAR в каталоге autodeploy сервера.

Если кто-то действительно не протестировал это, есть вероятность, что вы получите OutOfMemoryError после нескольких перераспределений, потому что приложение не позаботится о том, чтобы очистить себя. Вы можете даже найти ошибку на своем сервере с этим тестом.

Проблема в том, что срок службы контейнера превышает срок службы вашего приложения. Вы должны убедиться, что все ссылки, которые контейнер может иметь на объекты или классы вашего приложения, могут быть собраны в мусор.

Если есть только одна ссылка, сохранившаяся без развертывания вашего веб-приложения, соответствующий загрузчик классов и, следовательно, все классы вашего веб-приложения не могут быть собраны в мусор.

Темы, запущенные вашим приложением, переменные ThreadLocal, добавление журналов являются некоторыми из обычных подозреваемых, вызывающих утечки загрузчика.


Ссылка на объект статического поля [конечное поле esp]

class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

Вызов String.intern() для длинной строки

String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

(Unclosed) открытые потоки (файл, сеть и т. Д.)

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

Незакрытые соединения

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

Области, недоступные из сборщика мусора JVM , такие как память, выделенная с помощью собственных методов

В веб-приложениях некоторые объекты хранятся в области приложения до тех пор, пока приложение не будет явно остановлено или удалено.

getServletContext().setAttribute("SOME_MAP", map);

Неверные или несоответствующие параметры JVM , такие как опция noclassgc для IBM JDK, которая предотвращает неиспользуемую сборку мусора класса

См. Настройки IBM jdk .


Недавно я столкнулся с более тонким утечкой ресурсов. Мы открываем ресурсы через getResourceAsStream класса loader, и оказалось, что обработчики входного потока не были закрыты.

Эм, можно сказать, какой идиот.

Ну, что делает интересным это: таким образом, вы можете утечка памяти кучи лежащего в основе процесса, а не из кучи JVM.

Все, что вам нужно, это файл jar с файлом, внутри которого будет ссылаться код Java. Чем больше файл jar, тем быстрее распределяется память.

Вы можете легко создать такую ​​банку со следующим классом:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class BigJarCreator {
    public static void main(String[] args) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));
        zos.putNextEntry(new ZipEntry("resource.txt"));
        zos.write("not too much in here".getBytes());
        zos.closeEntry();
        zos.putNextEntry(new ZipEntry("largeFile.out"));
        for (int i=0 ; i<10000000 ; i++) {
            zos.write((int) (Math.round(Math.random()*100)+20));
        }
        zos.closeEntry();
        zos.close();
    }
}

Просто вставьте в файл BigJarCreator.java, скомпилируйте и запустите его из командной строки:

javac BigJarCreator.java
java -cp . BigJarCreator

Et voilà: вы найдете архив jar в своем текущем рабочем каталоге с двумя файлами внутри.

Давайте создадим второй класс:

public class MemLeak {
    public static void main(String[] args) throws InterruptedException {
        int ITERATIONS=100000;
        for (int i=0 ; i<ITERATIONS ; i++) {
            MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
        }
        System.out.println("finished creation of streams, now waiting to be killed");

        Thread.sleep(Long.MAX_VALUE);
    }

}

Этот класс в основном ничего не делает, но создает неопубликованные объекты InputStream. Эти объекты будут собирать мусор немедленно и, таким образом, не вносить вклад в размер кучи. Для нашего примера важно загрузить существующий ресурс из файла jar, и размер имеет значение здесь!

Если вы сомневаетесь, попробуйте скомпилировать и запустить класс выше, но обязательно выберите подходящий размер кучи (2 МБ):

javac MemLeak.java
java -Xmx2m -classpath .:big.jar MemLeak

Здесь вы не столкнетесь с ошибкой OOM, так как ссылки не сохраняются, приложение будет продолжать работать независимо от того, насколько большой вы выбрали Итерации в приведенном выше примере. Потребление памяти вашего процесса (видимое в верхнем (RES / RSS) или проводнике процессов) растет, если приложение не получает команду wait. В приведенной выше настройке он выделит около 150 МБ в памяти.

Если вы хотите, чтобы приложение играло безопасно, закройте входной поток прямо там, где он создан:

MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close();

и ваш процесс не будет превышать 35 МБ, независимо от количества итераций.

Довольно просто и удивительно.


Вот хороший способ создать настоящую утечку памяти (объекты, недоступные при запуске кода, но все еще хранящиеся в памяти) в чистой Java:

  1. Приложение создает длинный поток (или использует пул потоков, чтобы течь еще быстрее).
  2. Поток загружает класс через (необязательно настраиваемый) ClassLoader.
  3. Класс выделяет большой кусок памяти (например, new byte[1000000] ), сохраняет в нем сильную ссылку в статическом поле, а затем сохраняет ссылку на себя в ThreadLocal. Выделение дополнительной памяти необязательно (достаточно утечки экземпляра класса), но это сделает работу утечки намного быстрее.
  4. Нить очищает все ссылки на пользовательский класс или загрузчик ClassLoader, из которого он был загружен.
  5. Повторение.

Это работает, потому что ThreadLocal сохраняет ссылку на объект, который сохраняет ссылку на свой класс, который, в свою очередь, ссылается на свой ClassLoader. ClassLoader, в свою очередь, сохраняет ссылку на все загруженные классы.

(Это было хуже во многих реализациях JVM, особенно до Java 7, потому что Classes и ClassLoaders были распределены прямо в permgen и вообще не были GC'd. Однако независимо от того, как JVM обрабатывает разгрузку классов, ThreadLocal все равно будет препятствовать Объект класса от восстановления.)

Вариант этого шаблона заключается в том, почему контейнеры приложений (например, Tomcat) могут утечка памяти, например, сита, если вы часто передислоцируете приложения, которые каким-либо образом используют ThreadLocals. (Так как контейнер приложения использует потоки, как описано, и каждый раз, когда вы повторно развертываете приложение, используется новый ClassLoader.)

Обновление : поскольку многие люди продолжают его просить, вот пример кода, который показывает это поведение в действии .


Ответ полностью зависит от того, что, по мнению интервьюера, они спрашивали.

Возможно ли на практике сделать утечку Java? Конечно, это так, и в других ответах есть много примеров.

Но есть несколько мета-вопросов, которые могли быть заданы?

  • Является ли теоретически «идеальная» реализация Java уязвимой для утечек?
  • Разве кандидат понимает разницу между теорией и реальностью?
  • Понимает ли кандидат, как работает сбор мусора?
  • Или как сбор мусора должен работать в идеальном случае?
  • Знают ли они, что могут называть другие языки через собственные интерфейсы?
  • Знают ли они, что утечка памяти на этих других языках?
  • Кандидат даже знает, что такое управление памятью, и что происходит за сценой на Java?

Я читаю ваш мета-вопрос как «Какой ответ я мог бы использовать в этой ситуации интервью». И, следовательно, я собираюсь сосредоточиться на навыках собеседования вместо Java. Я верю, что вы с большей вероятностью повторите ситуацию, когда не знаете ответа на вопрос в интервью, чем вы должны быть в нужном месте, чтобы знать, как сделать утечку Java. Так что, надеюсь, это поможет.

Одним из наиболее важных навыков, которые вы можете разработать для опроса, является обучение активному прослушиванию вопросов и работа с интервьюером для извлечения их намерений. Это не только позволяет вам ответить на их вопрос так, как они хотят, но также показывает, что у вас есть важные навыки общения. И когда дело доходит до выбора между многими талантливыми разработчиками, я нанял того, кто слушает, думает и понимает, прежде чем они будут реагировать каждый раз.


Общим примером этого в GUI-коде является создание виджета / компонента и добавление слушателя к некоторому объекту с статическими / прикладными областями, а затем не удаление слушателя при уничтожении виджета. Вы получаете не только утечку памяти, но и производительность, так как, когда вы слушаете события пожаров, все ваши старые слушатели также вызываются.


У меня была хорошая «утечка памяти» по отношению к PermGen и синтаксическому анализу XML один раз. Используемый нами синтаксический анализатор XML (я не помню, какой из них был) использовал имена тегов String.intern (), чтобы ускорить сравнение. У одного из наших клиентов была отличная идея хранить значения данных не в атрибутах или тексте XML, а в качестве тэгов, поэтому у нас был такой документ:

<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>

Фактически, они не использовали числа, а более длинные текстовые идентификаторы (около 20 символов), которые были уникальными и приходили со скоростью 10-15 миллионов в день. Это делает 200 МБ мусора в день, что больше никогда не нужно, и никогда не GCed (так как он находится в Пермигене). У нас был набор пергентов размером до 512 МБ, поэтому для получения исключения из памяти (OOME) потребовалось около двух дней ...


Вы можете сделать утечку памяти с классом sun.misc.Unsafe . Фактически этот класс обслуживания используется в разных стандартных классах (например, в классах java.nio ). Вы не можете создать экземпляр этого класса напрямую , но вы можете использовать отражение для этого .

Код не компилируется в Eclipse IDE - скомпилируйте его с помощью команды javac (во время компиляции вы получите предупреждения)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import sun.misc.Unsafe;


public class TestUnsafe {

    public static void main(String[] args) throws Exception{
        Class unsafeClass = Class.forName("sun.misc.Unsafe");
        Field f = unsafeClass.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        System.out.print("4..3..2..1...");
        try
        {
            for(;;)
                unsafe.allocateMemory(1024*1024);
        } catch(Error e) {
            System.out.println("Boom :)");
            e.printStackTrace();
        }
    }

}

Вероятно, интервьюер искал круговую ссылку, как приведенный ниже код (который, кстати, только утечка памяти в очень старых JVM, которые использовали подсчет ссылок, что больше не так). Но это довольно неопределенный вопрос, так что это прекрасная возможность продемонстрировать ваше понимание управления памятью JVM.

class A {
    B bRef;
}

class B {
    A aRef;
}

public class Main {
    public static void main(String args[]) {
        A myA = new A();
        B myB = new B();
        myA.bRef = myB;
        myB.aRef = myA;
        myA=null;
        myB=null;
        /* at this point, there is no access to the myA and myB objects, */
        /* even though both objects still have active references. */
    } /* main */
}

Тогда вы можете объяснить, что при подсчете ссылок, приведенный выше код будет утечка памяти. Но большинство современных JVM больше не используют подсчет ссылок, большинство используют сборщик мусора, который фактически собирает эту память.

Затем вы можете объяснить создание объекта с базовым исходным ресурсом, например:

public class Main {
    public static void main(String args[]) {
        Socket s = new Socket(InetAddress.getByName("google.com"),80);
        s=null;
        /* at this point, because you didn't close the socket properly, */
        /* you have a leak of a native descriptor, which uses memory. */
    }
}

Тогда вы можете объяснить, что это технически утечка памяти, но на самом деле утечка вызвана собственным кодом в JVM, который выделяет основные ресурсы, которые не были освобождены вашим Java-кодом.

В конце дня, с помощью современной JVM, вам нужно написать некоторый Java-код, который выделяет собственный ресурс вне обычной области знаний JVM.


Java 9

В Java 9 вы можете использовать List.of static factory для создания литерала List . Что-то вроде следующего:

List<Element> elements = List.of(new Element(1), new Element(2), new Element(3));

Это вернет immutable список, содержащий три элемента. Если вы хотите изменить список, передайте этот список конструктору ArrayList :

new ArrayList<>(List.of(// elements vararg))

JEP 269: методы фабрики удобрений для коллекций

JEP 269 предоставляет некоторые удобные фабричные методы API Java Collections . Эти неизменные статические заводские методы встроены в интерфейсы List , Set и Map в Java 9 и более поздних версиях.





java memory memory-leaks