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

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

Каким будет пример?

+3044
источник поделиться
55 ответов
  • 1
  • 2

Другой способ создать потенциально большие утечки памяти - хранить ссылки на Map.Entry<K,V> TreeMap.

Трудно понять, почему это применимо только к TreeMap, но, глядя на реализацию, причина может быть в следующем: TreeMap.Entry хранит ссылки на своих братьев и сестер, поэтому, если TreeMap готов к сбору, но некоторые другие классы содержат ссылка на любой из его Map.Entry, тогда вся карта будет сохранена в памяти.


Реальный сценарий:

Представьте, что у вас есть запрос TreeMap данных, который возвращает большую структуру данных TreeMap. Люди обычно используют TreeMap поскольку порядок вставки элементов сохраняется.

public static Map<String, Integer> pseudoQueryDatabase();

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

Рассмотрим следующий класс-оболочку:

class EntryHolder {
    Map.Entry<String, Integer> entry;

    EntryHolder(Map.Entry<String, Integer> entry) {
        this.entry = entry;
    }
}

Заявка:

public class LeakTest {

    private final List<EntryHolder> holdersCache = new ArrayList<>();
    private static final int MAP_SIZE = 100_000;

    public void run() {
        // create 500 entries each holding a reference to an Entry of a TreeMap
        IntStream.range(0, 500).forEach(value -> {
            // create map
            final Map<String, Integer> map = pseudoQueryDatabase();

            final int index = new Random().nextInt(MAP_SIZE);

            // get random entry from map
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                if (entry.getValue().equals(index)) {
                    holdersCache.add(new EntryHolder(entry));
                    break;
                }
            }
            // to observe behavior in visualvm
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

    }

    public static Map<String, Integer> pseudoQueryDatabase() {
        final Map<String, Integer> map = new TreeMap<>();
        IntStream.range(0, MAP_SIZE).forEach(i -> map.put(String.valueOf(i), i));
        return map;
    }

    public static void main(String[] args) throws Exception {
        new LeakTest().run();
    }
}

После каждого pseudoQueryDatabase() экземпляры map должны быть готовы к сбору, но этого не произойдет, поскольку хотя бы одна Entry хранится где-то еще.

В зависимости от настроек jvm, приложение может OutOfMemoryError на ранней стадии из-за OutOfMemoryError.

Из этого графика visualvm как растет память.

Memory dump - TreeMap

То же самое не происходит с хешированной структурой данных (HashMap).

Это график при использовании HashMap.

Memory dump - HashMap

Решение? Просто сохраните ключ/значение (как вы, вероятно, уже сделали) вместо сохранения Map.Entry.


Я написал более обширный тест здесь.

+10
источник

Недавно я установил новый объект GC и Image, но забыл вызвать метод dispose().

GC javadoc snippet:

Код приложения должен явно ссылаться на метод GC.dispose(), чтобы освободить ресурсы операционной системы, управляемые каждым экземпляром, когда эти случаи больше не требуются. Это особенно важно в Windows95 и Windows98, где операционная система имеет ограниченную количество доступных контекстов устройства.

Изображение javadoc snippet:

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

+9
источник
другие ответы

Связанные вопросы


Похожие вопросы

Теоретически вы не можете. Модель памяти Java предотвращает это. Однако, поскольку Java должен быть реализован, есть некоторые предостережения, которые вы можете использовать. В зависимости от того, что вы можете использовать:

  • Если вы можете использовать native, вы можете выделить память, которую вы позже не откажете.

  • Если это не доступно, есть небольшая тайна о java, которую не так много знают люди. Вы можете запросить массив прямого доступа, который не управляется GC, и поэтому его можно легко использовать для утечки памяти. Это обеспечивается DirectByteBuffer (http://download.oracle.com/javase/1.5.0/docs/api/java/nio/ByteBuffer.html#allocateDirect(int)).

  • Если вы не можете использовать какие-либо из них, вы все равно можете сделать утечку памяти, обманув GC. JVM реализуется с использованием сборщика мусора Generational. Это означает, что куча разделена на области: молодые, взрослые и старейшины. Объект, когда его созданный начинается в молодой области. Поскольку он используется все больше и больше, он переходит к взрослым до старейшин. Объект, который, скорее всего, попадает в область пожилых людей, скорее всего, не будет собран в мусор. Вы не можете быть уверены, что объект просочился, и если вы попросите остановить и очистить GC, он может его очистить, но в течение длительного времени он будет просочиться. Дополнительная информация (http://java.sun.com/docs/hotspot/gc1.4.2/faq.html)

  • Кроме того, объекты класса не обязательно должны быть GC'ed. Могу ли я сделать это.

+8
источник

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

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

+8
источник

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

Вы должны контролировать потребление памяти Java в первую очередь.

Самый простой способ сделать это - использовать утилиту jstat, которая поставляется с JVM.

jstat -gcutil <process_id> <timeout>

Он будет сообщать о потреблении памяти для каждого поколения (Young, Eldery и Old) и времени сбора мусора (Young и Full).

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

Затем вам нужно создать дамп памяти с помощью утилиты jmap:

jmap -dump:live,format=b,file=heap.bin <process_id>

Затем вам нужно проанализировать файл heap.bin с помощью Memory Analyzer, например Eclipse Memory Analyzer (MAT).

MAT проанализирует память и предоставит вам подозрительную информацию об утечках памяти.

+8
источник

Большинство утечек памяти, которые я видел в java-процессах, выходят из синхронизации.

Процесс A разговаривает с B через TCP и сообщает процессу B что-то создать. B выдает ресурс ID, например 432423, который A хранит в объекте и использует во время разговора с B. В какой-то момент объект в восстанавливается сбором мусора (возможно, из-за ошибки), но A никогда не сообщает B, что ( возможно, другая ошибка).

Теперь A не имеет идентификатора объекта, который он создал в B RAM, и B не знает, что A больше не ссылается на объект. По сути, объект просочился.

+7
источник

Несколько предложений:

  • использовать commons-logging в контейнере сервлетов (возможно, немного провокационный)
  • запустите поток в контейнере сервлета и не вернетесь из него.
  • загружать анимированные gifs в контейнер сервлетов (это запустит поток анимации)

Вышеуказанные эффекты могут быть "улучшены" путем перераспределения приложения;)

Недавно наткнулся на это:

  • Вызов "new java.util.zip.Inflater();" без вызова "Inflater.end()" ever

Прочитайте http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5072161 и увязывайте вопросы для углубленного обсуждения.

+7
источник

Если максимальный размер кучи X. Y1.... Yn нет экземпляров Итак, общая память = количество экземпляров X байтов на экземпляр. Если X1...... Xn - это байты на экземпляры. Затем общая память ( M) = Y1 * X1 +..... + Yn * Xn. Итак, если M > X, оно превышает пустое пространство. следующие могут быть проблемы в коде 1. Используйте больше экземпляров переменной, чем локальную. 2.Создание экземпляров каждый раз вместо объединения объекта. 3. Не создавать объект по требованию. 4. Создание ссылки на объект null после завершения операции. Возможен, воссоздание, когда оно требуется в программе.

+7
источник

Выбросить необработанное исключение из метода finalize.

+7
источник

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

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

Если я спрошу вас: "У вас есть опыт устранения неполадок утечки памяти в Java?", ваш ответ будет простым "Да". Затем я должен был бы проследить за "Не могли бы вы привести примеры, где вы хотите устранить утечки памяти?", На которые вы могли бы привести мне один или два примера.

Однако, когда интервьюер спрашивает "как создать утечку памяти с помощью Java?" ожидаемый ответ должен следовать следующим строкам:

  • Я столкнулся с утечкой памяти... (скажем, когда) [показывает мне опыт]
  • Код, вызывающий это, был... (объясните код) [вы исправили его самостоятельно]
  • Исправление, к которому я применил, основывалось на... (объясните исправление) [это дает мне возможность задать специфику исправления]
  • Тест, который я сделал, был... [дает мне возможность просить другие методологии тестирования]
  • Я зарегистрировал его таким образом... [дополнительные очки. Хорошо, если вы задокументировали это.
  • Итак, разумно подумать, что если мы будем следовать этому в обратном порядке, то есть получить код, который я исправил, и удалить мое исправление, что у нас будет утечка памяти.

Когда разработчик не выполняет эту мысль, я пытаюсь спросить его: "Не могли бы вы привести мне пример того, как может произойти утечка памяти Java?", а затем "У вас когда-либо было исправление утечки памяти в Java?"

Обратите внимание, что я не запрашиваю пример о том, как утечка памяти в Java. Это было бы глупо. Кому будет интересен разработчик, который может эффективно писать код, который утечки памяти?

+7
источник

Одна из возможностей - создать оболочку для ArrayList, которая предоставляет только один метод: тот, который добавляет вещи в ArrayList. Сделайте сам ArrayList приватным. Теперь создайте один из этих объектов-оболочек в глобальной области (как статический объект в классе) и квалифицируйте его ключевым словом final (например, public static final ArrayListWrapper wrapperClass = new ArrayListWrapper()). Так что теперь ссылка не может быть изменена. То есть wrapperClass = null не будет работать и не может использоваться для освобождения памяти. Но также нет никакого способа сделать что-либо с wrapperClass, кроме добавления к нему объектов. Поэтому любые объекты, которые вы добавляете в wrapperClass, невозможно переработать.

+6
источник

Утечка памяти в Java не является типичной утечкой памяти в C/C++.

Чтобы понять, как работает JVM, прочитайте раздел "Управление памятью".

В основном, важная часть:

Модель Марка и Сметания

JRockit JVM использует модель сборки мусора меток и зачисток для выполнения сборок мусора всей кучи. Сбор мусора с меткой и уборкой состоит из двух фаз: фазы маркировки и фазы уборки.

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

Во время фазы развертки проходит куча, чтобы найти промежутки между живыми объектами. Эти пробелы записываются в свободном списке и становятся доступными для размещения новых объектов.

В JRockit JVM используются две улучшенные версии модели mark и sweep. Один в основном совпадает с меткой и разверткой, а другой - с параллельной меткой и разверткой. Вы также можете смешать две стратегии, например, в основном для одновременной метки и параллельной развертки.

Итак, чтобы создать утечку памяти в Java; самый простой способ сделать это - создать соединение с базой данных, выполнить некоторую работу и просто не Close(); затем создайте новое соединение с базой данных, оставаясь в области видимости. Это не сложно сделать в цикле, например. Если у вас есть работник, который извлекает данные из очереди и передает их в базу данных, вы можете легко создать утечку памяти, забыв о соединениях Close() или открыв их, когда это не нужно, и так далее.

В конце концов, вы будете использовать кучу, выделенную для JVM, забыв Close() соединение. Это приведет к сбору мусора в JVM как сумасшедшему; в конечном итоге приводит к java.lang.OutOfMemoryError: Java heap space ошибки java.lang.OutOfMemoryError: Java heap space. Следует отметить, что ошибка может не означать утечку памяти; это может означать, что у вас недостаточно памяти; базы данных, такие как Cassandra и ElasticSearch, например, могут выдавать эту ошибку, потому что у них недостаточно места в куче.

Стоит отметить, что это верно для всех языков GC. Ниже приведены некоторые примеры работы в качестве SRE:

  • Узел, использующий Redis в качестве очереди; Команда разработчиков создавала новые соединения каждые 12 часов и забыла закрыть старые. В конце концов узел был OOMd, потому что он занимал всю память.
  • Голанг (я виноват в этом); парсинг больших файлов json с json.Unmarshal а затем передача результатов по ссылке и сохранение их открытыми. В конечном итоге это привело к тому, что вся куча была поглощена случайными ссылками, которые я оставил открытыми для декодирования json.
+6
источник

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

Итак, ответьте на этот вопрос, как будто они спрашивают, что увеличивает использование памяти JVM с течением времени. Хорошие ответы будут хранить слишком много данных в HttpSessions с чрезмерно длинным таймаутом или плохо реализованным кэшем в памяти (Singleton), который никогда не сбрасывает старые записи. Другим потенциальным ответом является наличие множества JSP или динамически генерируемых классов. Классы загружаются в область памяти PermGen, которая обычно мала, и большинство JVM не реализуют разгрузку классов.

+5
источник

У Swing очень простое диалоговое окно. Создайте JDialog, покажите его, пользователь закрывает его, течет! Вы должны вызвать dispose() или настроить setDefaultCloseOperation(DISPOSE_ON_CLOSE)

+5
источник

Метод String.substring в java 1.6 создает утечку памяти. Это сообщение в блоге объясняет это.

http://javarevisited.blogspot.com/2011/10/how-substring-in-java-works.html

+5
источник

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

+4
источник

Lapsed Listerners - хороший пример утечек памяти: объект добавляется как слушатель. Все ссылки на объект обнуляются, когда объект больше не нужен. Однако, забыв удалить объект из списка Listener, он сохраняет объект и даже реагирует на события, тем самым теряя память и процессор. См. http://www.drdobbs.com/jvm/java-qa/184404011

+4
источник

Небрежно Использование нестатического внутреннего класса внутри класса, у которого есть свой жизненный цикл.

В Java нестатические внутренние и анонимные классы содержат неявные ссылки для своего внешнего класса. Статические внутренние классы, с другой стороны, не.

Вот типичный пример утечки памяти в Android, что не очевидно:

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() { //non-static inner class, holds the reference to the SampleActivity outter class
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for a long time.
    mLeakyHandler.postDelayed(new Runnable() {//here, the anonymous inner class holds the reference to the SampleActivity class too
      @Override
      public void run() {
     //.... 
      }
    }, SOME_TOME_TIME);

    // Go back to the previous Activity.
    finish();
  }}

Это предотвратит сбор контекста активности.

+4
источник

Пример утечки памяти в реальном времени до JDK 1.7

Предположим, вы читаете файл из 1000 строк текста и сохраняете объект String

String fileText = 1000 characters from file

fileText = fileText.subString(900, fileText.length());

В приведенном выше коде я сначала прочитал 1000 символов, а затем сделал подстроку, чтобы получить только 100 последних символов. Теперь fileText должен ссылаться только на 100 символов, и все другие символы должны собирать мусор, так как я потерял ссылку, но до того, как функция подстроки JDK 1.7 косвенно ссылается на исходную строку из последних 100 символов, она предотвращает сбор всей строки и сборку из 1000 символов. в памяти, пока вы не потеряете ссылку на подстроку.

Вы можете создать пример утечки памяти, как указано выше

0
источник

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

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

Первым примером должен быть пользовательский стек без нуля устаревших ссылок в эффективном элементе java 6.

Конечно, есть еще столько, сколько вы хотите, но если мы просто посмотрим на встроенные классы Java, это может быть как

subList()

Давайте проверим некоторый супер глупый код, чтобы произвести утечку.

public class MemoryLeak {
    private static final int HUGE_SIZE = 10_000;

    public static void main(String... args) {
        letsLeakNow();
    }

    private static void letsLeakNow() {
        Map<Integer, Object> leakMap = new HashMap<>();
        for (int i = 0; i < HUGE_SIZE; ++i) {
            leakMap.put(i * 2, getListWithRandomNumber());
        }
    }



    private static List<Integer> getListWithRandomNumber() {
        List<Integer> originalHugeIntList = new ArrayList<>();
        for (int i = 0; i < HUGE_SIZE; ++i) {
            originalHugeIntList.add(new Random().nextInt());
        }
        return originalHugeIntList.subList(0, 1);
    }
}

На самом деле есть еще одна хитрость, которую мы можем вызвать утечку памяти, используя HashMap, воспользовавшись преимуществами его процесса поиска. На самом деле есть два типа:

  • hashCode() всегда одинаков, но equals() разные;
  • используйте случайные hashCode() и equals() всегда true;

Почему?

hashCode() → bucket => equals(), чтобы найти пару


Я собирался сначала упомянуть substring(), а затем subList(), но, похоже, эта проблема уже исправлена, поскольку ее источник представлен в JDK 8.

public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
}
0
источник

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

while(true) {
    ResultSet rs = database.select(query);
    ...
    // going to next step of loop and leaving resultset without calling rs.close();
}
0
источник

Просто так!

public static void main(String[] args) {
    List<Object> objects = new ArrayList<>();
    while(true) {
        objects.add(new Object());
    }
}
-3
источник

Из эффективной java-книги

  • , когда класс управляет собственной памятью, программист должен предупреждение о утечке памяти

.

public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {
    elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
}

public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    return elements[--size];
}

/**
 * Ensure space for at least one more element, roughly doubling the capacity
 * each time the array needs to grow.
 */
private void ensureCapacity() {
    if (elements.length == size)
        elements = Arrays.copyOf(elements, 2 * size + 1);
}

}

Вы можете обнаружить утечку памяти? Так где же утечка памяти? Если стек растет, а затем сжимается, объекты которые выскочили со стека, не будут собирать мусор, даже если программа использование стека не имеет больше ссылок на них. Это связано с тем, что стек поддерживает устаревшие ссылки на эти объекты. Устаревшая ссылка - это просто ссылка это никогда не будет разыменовано снова. В этом случае любые ссылки за пределами "активная часть" массива элементов устарела. Активная часть состоит из элементов, индекс которых меньше размера.

-4
источник

Вот очень простая Java-программа, которая выйдет из пространства

public class OutOfMemory {

    public static void main(String[] arg) {

        List<Long> mem = new LinkedList<Long>();
        while (true) {
            mem.add(new Long(Long.MAX_VALUE));
        }
    }
}
-5
источник

В Java нет такой вещи, как утечка памяти. Утечка памяти - это фраза, заимствованная у C et al. Java имеет дело с распределением памяти внутри с помощью GC. Там расточительность памяти (т.е. Оставление многожильных объектов), но не утечка памяти.

-5
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос