在一次采访中有人问我这个问题。
有一个类,可以称之为A
。它具有默认的构造函数,并在其构造函数中初始化2个不同的连接。
方法initDB
和initSocket
创建一个DB连接和一个套接字连接,并将它们保存在实例字段中。 它们只是一个例子。它们也可以打开文件或其他任何方式。
让我们说一个客户实例化了这个类的一个实例。现在initDB
成功执行了,但是initSocket
引发了异常。因此对象创建被中止了。但是在异常发生之前,数据库连接没有关闭。这导致资源泄漏。我该如何解决这种资源泄漏?
例如
class A {
public A(){
this.dbConnection = initDB();
this.socketConnection = initSocket(); // throws exception
}
}
我最初的回答是,我不会在构造函数中初始化它们,而是在单独的init()
中初始化它们。他反驳说这可能是一个遗留类,我被要求保持这种状态。在这种情况下,我需要以某种方式清除资源泄漏。我将如何解决这个问题?
我很沮丧,因为实例创建引发了异常,我失去了对这些连接字段的任何引用。因此,我无法致电close()
。但是它们仍然会出现在操作系统级别(这是我的猜测)。
注释1
如Interviewer所述,我无法更改已编写的构造函数的行为。我可以对其进行扩展或执行某些操作,但是不能更改代码。
注释2
我认为面试官没有明确地寻找可以处理这种情况的任何代码。有什么JMX东西有帮助吗?我对此很赞,然后我们继续前进。对于那些认为这是一个很好的问题的人,我认为采访者知道这不是一种普遍的做法,并且可能无法回答。
答案 0 :(得分:0)
我们在这里有一些选择...
将东西放在其他地方。这显然是有问题的遗留代码。也许这个具有访问控制问题的“斑点”可以移到其他一些可以通过RPC与系统其余部分通信的进程中。如果系统严重损坏,则最好这样做。您可以通过其他方式扩展它,例如合成。但是,如果它是如此封闭,您将无法获得它,那您就筋疲力尽了
使用字节码修改。您可以做到这一点,并且可以获得足够的杠杆来获得所需的东西。 ByteBuddy可以派上用场。我不会亲自这样做,但是,嘿,有时候绝望的措施需要绝望的解决方案...
如果可以影响initDB
,则可以用其他东西修饰返回值。例如,假设它来自我们 did 控制的某个基类或我们控制的其他方法,那么我们也许可以做类似的事情
Connection initDb() {
try {
this.wrappedProvider.initDb();
} catch(Exception e) {
// .. destroy the connection...
}
}
...,然后由于可以影响它,因此可以更改其有效语义。
答案 1 :(得分:0)
解决方案1:
提供:
initSocket
是可重写的(不是最终的也不是私有的)dbConnection
可从B类(非私有)访问在出现异常的情况下,您可以覆盖方法initSocket
以关闭dbConnection
:
@Override
protected Socket initSocket() {
boolean ok = false;
try {
Socket result = super.initSocket();
ok = true;
return result;
} finally {
if (!ok) {
dbConnection.close();
}
}
}
解决方案2:
提供:
initDb
是可重写的(不是最终的也不是私有的)您可以将A对象包装在另一个类中,并保存连接,以便在发生异常时可以将其关闭:
class B {
private static ThreadLocal<Connection> CONNECTION = new ThreadLocal<>();
private final A delegate;
public B() {
boolean ok = false;
try {
delegate = new A() {
@Override
protected Connection initDb() {
Connection result = super.initDb();
CONNECTION.set(result);
return result;
}
};
ok = true;
} finally {
if (!ok) {
Connection cnt = CONNECTION.get();
if (cnt != null) {
cnt.close();
}
}
CONNECTION.set(null);
}
}
}