我遇到了我的某个应用程序的问题,并在处理涉及打开文件和阅读内容的大量事务时遇到以下异常:
java.io.FileNotFoundException: /keystore-sample/keystore.jks (Too many open files)
我有以下用java编写的示例代码:
try {
EncryptDecryptPassword crypt = new EncryptDecryptPassword();
String secretKey = "secret123";
String trustpassencrypted = "ecnrypted1234";
String trustpass = crypt.decryptPassword(trustpassencrypted.trim(), secretKey.trim());
File truststore = new File("/keystore-sample/keystore.jks");
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(truststore), trustpass.toCharArray());
//Continue with the rest of the code...
} catch (KeyStoreException kse) {
System.out.println("Security configuration failed with the following: " + kse.getCause());
} catch (NoSuchAlgorithmException nsa) {
System.out.println("Security configuration failed with the following: " + nsa.getCause());
} catch (FileNotFoundException fnfe) {
System.out.println("Security configuration failed with the following: " + fnfe.getCause());
} catch (UnrecoverableKeyException uke) {
System.out.println("Security configuration failed with the following: " + uke.getCause());
} catch (CertificateException ce) {
System.out.println("Security configuration failed with the following: " + ce.getCause());
}catch (IOException ioe) {
System.out.println("Security configuration failed with the following: " + ioe.getCause());
}
如您所见,我没有为FileInput流声明一个新实例,如下所示:
更正:我的目的是表示我没有将新创建的FileInputStream分配给变量并稍后手动关闭它,如下所示:
FileInputStream in = new FileInputStream(truststore);
keyStore.load(in, trustpass.toCharArray());
//once done with the input stream, close it
in.close();
我的问题是:新的FileInputStream(truststore)是否真的需要手动关闭,否则它将由底层Keystore类处理?快速浏览Keystore.class的底层反编译代码,我没有看到。只是为了确认这是否是我遇到异常的特殊原因。
此外,上面的代码实现被认为是不好的做法吗?
修改
由于我正在运行的应用程序环境有一些限制,这是使用旧的Java SE 6u34。
答案 0 :(得分:3)
我没有为FileInput流声明一个新实例
这句话毫无意义。没有“声明新实例”这样的事情。您已经创建一个新实例,您只是没有声明一个引用变量来存储它。这并不能解除您的负担。新实例,或关闭它的责任。
我的问题是:新的FileInputStream(truststore)实际上是否需要手动关闭
是
或它将由底层Keystore类处理?
没有
快速浏览Keystore.class的底层反编译代码,我没有看到。只是为了确认这是否是我遇到异常的特殊原因。
'打开的文件太多':您正在泄漏文件描述符,无论是在这里还是其他地方。
此外,上面的代码实现被认为是不好的做法吗?
是的,因为你根本没有关闭FileInputStream
。请注意,在keyStore.load()
之后关闭它是不够的,因为这可能会引发异常。
这些天你会使用try-with-resources:
File truststore = new File("/keystore-sample/keystore.jks");
try (FileInputStream fis = new FileInputStream(truststore))
{
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(fis, ...);
}
即使有异常也会自动关闭它。
答案 1 :(得分:1)
你没有"声明一个新实例"是无关紧要的 - 关键是你通过new
创建一个实例。理解这一点至关重要 - 即使您没有通过将其分配给变量来保留该实例的句柄,您仍然可以创建它。
一般情况下,如果您创建它,您有责任关闭它。既然你说你不能使用Java 7的try-with-resources,你可以使用在finally块中关闭创建资源的旧模式。
InputStream trust = null;
try {
EncryptDecryptPassword crypt = new EncryptDecryptPassword();
String secretKey = "secret123";
String trustpassencrypted = "ecnrypted1234";
String trustpass = crypt.decryptPassword(trustpassencrypted.trim(), secretKey.trim());
File truststore = new File("/keystore-sample/keystore.jks");
KeyStore keyStore = KeyStore.getInstance("JKS");
trust = new FileInputStream(truststore)
keyStore.load(trust, trustpass.toCharArray());
//Continue with the rest of the code...
} catch (Exception e) {
// note that logging the stack trace is generally a better practice!
System.out.println("Security configuration failed with the following: " + e.getCause());
} finally {
try { // because close can throw an exception
if (trust != null) trust.close();
} catch (IOException ignored) {}
}
此外,由于您在所有例外情况下做同样的事情,列出所有例外情况的唯一原因是,如果您觉得它增加了代码的可读性(个人而言,我不会)通过明确指出您期望的例外情况。或者,再次根据您使用的Java版本,您可以将它们全部列在catch块中,用|分隔。