Считайте объект объявленным в методе:
public void foo() {
final Object obj = new Object();
// A long run job that consumes tons of memory and
// triggers garbage collection
}
obj подвергнется сборке "мусора" перед нечто () возвраты?
ОБНОВЛЕНИЕ: Ранее я думал, что obj не подвергается сборке "мусора" до нечто () возвраты.
Однако сегодня я оказываюсь неправильно.
Я имею, проводят несколько часов в исправлении ошибки и наконец нашел, что проблема вызывается собравшим "мусор" obj!
Кто-либо может объяснить, почему это происходит? И если я хочу, чтобы obj был прикреплен, как достигнуть его?
Вот код, который имеет проблему.
public class Program
{
public static void main(String[] args) throws Exception {
String connectionString = "jdbc:mysql://<whatever>";
// I find wrap is gc-ed somewhere
SqlConnection wrap = new SqlConnection(connectionString);
Connection con = wrap.currentConnection();
Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
ResultSet rs = stmt.executeQuery("select instance_id, doc_id from
crawler_archive.documents");
while (rs.next()) {
int instanceID = rs.getInt(1);
int docID = rs.getInt(2);
if (docID % 1000 == 0) {
System.out.println(docID);
}
}
rs.close();
//wrap.close();
}
}
После запущения программы Java это распечатает следующее сообщение, прежде чем это откажет:
161000
161000
********************************
Finalizer CALLED!!
********************************
********************************
Close CALLED!!
********************************
162000
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
И вот является код класса SqlConnection:
class SqlConnection
{
private final String connectionString;
private Connection connection;
public SqlConnection(String connectionString) {
this.connectionString = connectionString;
}
public synchronized Connection currentConnection() throws SQLException {
if (this.connection == null || this.connection.isClosed()) {
this.closeConnection();
this.connection = DriverManager.getConnection(connectionString);
}
return this.connection;
}
protected void finalize() throws Throwable {
try {
System.out.println("********************************");
System.out.println("Finalizer CALLED!!");
System.out.println("********************************");
this.close();
} finally {
super.finalize();
}
}
public void close() {
System.out.println("********************************");
System.out.println("Close CALLED!!");
System.out.println("********************************");
this.closeConnection();
}
protected void closeConnection() {
if (this.connection != null) {
try {
connection.close();
} catch (Throwable e) {
} finally {
this.connection = null;
}
}
}
}
Поскольку ваш код написан , объект, на который указывает «wrap», не должен иметь право на сборку мусора, пока «wrap» не появится из стека в конце метода.
Тот факт, что он собирается, подсказывает мне, что ваш код , скомпилированный , не соответствует исходному источнику, и что компилятор провел некоторую оптимизацию, например, изменив это:
SqlConnection wrap = new SqlConnection(connectionString);
Connection con = wrap.currentConnection();
на следующее:
Connection con = new SqlConnection(connectionString).currentConnection();
(или даже встраивание всего этого), потому что за пределами этого пункта термин «перенос» не используется. Созданный анонимный объект сразу получит право на сборку мусора.
Единственный способ быть уверенным - это декомпилировать код и посмотреть, что с ним сделали.
Здесь obj
- это локальная переменная в методе, которая извлекается из стека, как только метод возвращается или завершается. Это не оставляет возможности достичь объекта Object
в куче, и, следовательно, он будет собран сборщиком мусора. А объект Object
в куче будет GC'd только после того, как его ссылка obj
будет извлечена из стека, то есть только после того, как метод завершится или вернется.
РЕДАКТИРОВАТЬ:
Чтобы ответить на ваше обновление,
UPDATE: Let me make the question more clear.
Will obj be subject to garbage collection before foo() returns?
obj
- это просто ссылка на фактический объект на куча . Здесь obj
объявлен внутри метода foo (). Итак, ваш вопрос Будет ли obj подвергаться сборке мусора до того, как foo () вернется?
не применяется, поскольку obj
входит в кадр стека, когда метод foo ()
является выполняется и исчезает, когда метод завершается.
Согласно текущей спецификации, не существует даже порядка «происходит до» от завершения до нормального использования. Итак, чтобы навести порядок, вам действительно нужно использовать блокировку, изменчивую переменную или, если вы в отчаянии, спрятать ссылку, доступную из статики. В размахе, конечно, нет ничего особенного.
Вряд ли вам действительно понадобится написать финализатор.
На самом деле здесь происходят две разные вещи. obj
- это переменная стека, для которой устанавливается ссылка на объект
, а объект
размещается в куче. Стек будет просто очищен (арифметикой указателя стека).
Но да, сам объект
будет очищен сборкой мусора. Все объекты, размещенные в куче, подлежат сборке мусора.
РЕДАКТИРОВАТЬ: Чтобы ответить на ваш более конкретный вопрос, спецификация Java не гарантирует сбор по в любое конкретное время (см. спецификацию JVM ) (конечно, он будет собран ] после его последнего использования). Так что ответить можно только для конкретных реализаций.
РЕДАКТИРОВАТЬ: Уточнение, в комментариях
Как я уверен, вы знаете, в Java сборка мусора и финализация не являются детерминированными. Все, что вы можете определить в этом случае, - это когда wrap
является подходящим для сборки мусора. Я предполагаю, что вы спрашиваете, становится ли wrap
подходящим для GC только тогда, когда метод возвращает (а wrap
выходит за рамки). Я думаю, что некоторые JVM (например,HotSpot с -сервер
) не будет ждать, пока ссылка на объект будет извлечена из стека, он сделает его подходящим для GC, как только на него больше ничего не ссылается. Похоже, это то, что вы видите.
Подводя итог, вы полагаетесь на то, что завершение будет достаточно медленным, чтобы не завершить экземпляр SqlConnection
до выхода из метода. Финализатор закрывает ресурс, за который SqlConnection
больше не отвечает. Вместо этого вы должны позволить объекту Connection
отвечать за свое завершение.
Я искренне поражен этим, но вы правы. Это легко воспроизвести, вам не нужно возиться с подключениями к базе данных и тому подобным:
public class GcTest {
public static void main(String[] args) {
System.out.println("Starting");
Object dummy = new GcTest(); // gets GC'd before method exits
// gets bigger and bigger until heap explodes
Collection<String> collection = new ArrayList<String>();
// method never exits normally because of while loop
while (true) {
collection.add(new String("test"));
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("Finalizing instance of GcTest");
}
}
Работает с:
Starting
Finalizing instance of GcTest
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at test.GcTest.main(GcTest.java:22)
Как я уже сказал, мне трудно в это поверить, но отрицать доказательства невозможно.
Правда, в этом есть извращенный смысл: ВМ выясняет, что объект никогда не используется, и избавляется от него. Это должно быть разрешено спецификацией.
Возвращаясь к коду вопроса, вы никогда не должны полагаться на finalize()
для очистки ваших соединений, вы должны всегда делать это явно.