Ниже приведен довольно бессмысленный пример, если вы не понимаете 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
, могут просто не быть возвращены.
В таком случае, когда метод Connection
finalize
не очищает все, можно обнаружить, что физическое соединение с сервером базы данных будет длиться несколько циклов сбора мусора, пока сервер базы данных в конечном итоге не выяснит что соединение не является активным (если оно существует) и должно быть закрыто.
Даже если драйвер JDBC должен был реализовывать finalize
, исключения могут создаваться во время финализации. В результате получается, что любая память, связанная с теперь «неактивным» объектом, не будет возвращена, поскольку finalize
гарантированно будет вызываться только один раз.
Приведенный выше сценарий возникновения исключений во время завершения объекта связан с другим другим сценарием, который может привести к утечке памяти - воскрешению объекта. Воскресение объекта часто делается намеренно, создавая сильную ссылку на объект из финализируемого объекта. При неправильном использовании воскресения объекта это приведет к утечке памяти в сочетании с другими источниками утечек памяти.
Есть еще много примеров, которые вы можете придумать - например,
- Управление экземпляром
List
, когда вы только добавляете в список, а не удаляете из него (хотя вы должны избавляться от элементов, которые вам больше не нужны), или
- Открытие
Socket
с или File
с, но не закрытие их, когда они больше не нужны (аналогично приведенному выше примеру с классом Connection
).
- Не выгружать Singletons при закрытии приложения Java EE. По всей видимости, загрузчик классов, который загрузил класс singleton, сохранит ссылку на класс, и, следовательно, экземпляр singleton никогда не будет собран. При развертывании нового экземпляра приложения обычно создается новый загрузчик классов, и прежний загрузчик классов продолжает существовать из-за синглтона.