SiL*_*oNG 5 java garbage-collection
考虑一个方法中声明的对象:
public void foo() {
final Object obj = new Object();
// A long run job that consumes tons of memory and
// triggers garbage collection
}
Run Code Online (Sandbox Code Playgroud)
在 foo()返回之前 obj会被垃圾收集吗?
更新:以前我认为obj不受垃圾收集的影响,直到 foo()返回.
但是,今天我发现自己错了.
我花了几个小时来修复一个bug,最后发现问题是由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();
}
}
Run Code Online (Sandbox Code Playgroud)
运行Java程序后,它会在崩溃之前打印以下消息:
161000
161000
********************************
Finalizer CALLED!!
********************************
********************************
Close CALLED!!
********************************
162000
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
Run Code Online (Sandbox Code Playgroud)
这是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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我对此感到非常惊讶,但你是对的.它很容易重现,你不需要弄乱数据库连接等:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
运行:
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)
Run Code Online (Sandbox Code Playgroud)
就像我说的,我简直不敢相信,但不可否认证据.
尽管如此,它确实会产生一种反常的感觉,VM会发现该对象从未被使用过,因此将其删除.这必须由规范允许.
回到问题的代码,你永远不应该依赖finalize()清理你的连接,你应该总是明确地做.
在编写代码时,“wrap”指向的对象不符合垃圾回收条件,直到“wrap”在方法末尾从堆栈中弹出。
它被收集的事实表明,您编译的代码与原始源代码不匹配,并且编译器已经做了一些优化,例如更改此内容:
SqlConnection wrap = new SqlConnection(connectionString);
Connection con = wrap.currentConnection();
Run Code Online (Sandbox Code Playgroud)
对此:
Connection con = new SqlConnection(connectionString).currentConnection();
Run Code Online (Sandbox Code Playgroud)
(或者甚至内联整个内容)因为超出这一点就不再使用“wrap”。创建的匿名对象将立即有资格进行 GC。
唯一确定的方法是反编译代码并查看对其做了什么。
| 归档时间: |
|
| 查看次数: |
720 次 |
| 最近记录: |