Ответ 1
Два ключевых шаблона "эффективной утечки" в моем опыте:
- Статика и синглеты, которые постепенно растут с течением времени. Это могут быть кеши, плохо реализованные и используемые пулы соединений, словари "каждого пользователя, которого мы видели с момента запуска" и т.д.
- Ссылки на долгоживущие объекты на объекты, которые были предназначены для недолговечности. В С# это может случиться с событиями, и эквивалентный шаблон наблюдателя может дать такой же эффект в Java. В принципе, если вы попросите один объект (наблюдателя) наблюдать за другим (источником), то обычно вы получаете ссылку от источника на наблюдателя. Это может стать единственной "живой" ссылкой, но она будет жить до тех пор, пока источник.
- Устраняет утечку, если вы продолжаете генерировать новый код динамически. Я нахожусь здесь на скалистом грунте, но я уверен, что столкнулся с такими проблемами. Возможно, это было частично связано с ошибками JRE, которые с тех пор были исправлены - это было слишком долго, поскольку это случилось для меня, чтобы точно запомнить.
- Единичные тесты, которые удерживаются в состоянии, могут длиться дольше, чем вы могли ожидать, потому что JUnit будет удерживать экземпляры тестового набора. Опять же, я не могу вспомнить детали, но иногда это заставляет иметь явное "обнуление переменной" в слежении, анахроничное, как это выглядит.
Я не могу сказать, что я регулярно обнаруживал утечки памяти в качестве проблемы в Java (или .NET).