简介
我在网上找到了很多关于太多打开文件异常的信息,但我无法解决这个奇怪的情况。正如我所读到的,当超过OS中定义的进程打开的文件描述符的数量时,抛出异常。这些文件的性质是多种多样的。文件可以是套接字,文档等。我已经找到了强大而安全的方法来打开我在Java应用程序中实现的文件。
该应用程序是一个使用Boilerpipe算法下载网页的简短程序。这样我就可以获得该网站最具代表性的内容。然后,我以适当的格式( TREC格式)将其写入磁盘。这些网站的URL取自我使用 JDBC连接器访问的MySQL数据库。
所以,我认为异常可以从三个不同的地方抛出:
虽然,正如我所说,我认为我使用正确的方式打开和编写这些文件。
问题
需要处理数千个URL,一段时间后会抛出异常(使调试也非常困难...... )。我不知道这是否重要,但URL被分为不同的类别,我运行不同的程序实例来加速整个过程。类别不重叠,所以不应该有任何问题。
代码
为了使其更具可读性,我将简单地展示我的代码的这三部分:
数据库访问
// Connect to database
Connection dbconn = null;
try {
String dbUrl = "jdbc:mysql://" + dbServer + "/" + dbName;
Class.forName ("com.mysql.jdbc.Driver").newInstance ();
dbconn = DriverManager.getConnection(dbUrl, dbUser, dbPass);
System.out.println ("Database connection established");
} catch (Exception e) {
e.printStackTrace();
System.err.println ("Cannot connect to database server");
System.exit(-1);
}
System.out.println(" Downloading category: " + category);
Statement s = null;
try {
s = dbconn.createStatement();
} catch (SQLException e) {
System.err.println ("Error on creating the statement");
System.exit(-1);
e.printStackTrace();
}
String q = "SELECT resource,topic FROM " +
"content_links " +
"WHERE topic LIKE 'Top/" + category + "%';";
try {
s.executeQuery(q);
} catch(Exception e) {
System.err.println ("Error on executing the SQL statement");
System.exit(-1);
e.printStackTrace();
}
ResultSet rs = null;
try {
rs = s.getResultSet ();
} catch (SQLException e) {
System.err.println ("Error on getting the result set");
System.exit(-1);
e.printStackTrace();
}
int count = 0, webError = 0;
// work with the result set
try {
while (rs.next ()) {
// MAIN LOOP
}
} catch (SQLException e) {
System.err.println ("Error on getting next item");
System.exit(-1);
e.printStackTrace();
}
// Close connection to database
if (dbconn != null) {
try {
dbconn.close ();
System.out.println (" Database connection terminated");
} catch (Exception e) { /* ignore close errors */ }
}
HTTP连接,提取网站标题和样例过滤器
try {
String title = "";
org.jsoup.nodes.Document doc = Jsoup.connect(urlVal).get();
for (Element element : doc.select("*")) {
if (element.tagName().equalsIgnoreCase("title")) {
title = element.text();
}
if (!element.hasText() && element.isBlock()) {
element.remove();
}
}
String contents = "";
contents = NumWordsRulesExtractor.INSTANCE.getText(doc.text());
storeFile(id, urlVal, catVal, title, contents);
}
} catch (BoilerpipeProcessingException e) {
System.err.println("Connection failed to: " + urlVal);
} catch (MalformedURLException e1) {
System.err.println("Malformed URL: " + urlVal);
} catch(Exception e2) {
System.err.println("Exception: " + e2.getMessage());
e2.getStackTrace();
}
写文件
private static void storeFile(String id, String url, String cat, String title, String contents) {
BufferedWriter out = null;
try {
out = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(
new File(path + "/" + id + ".webtrec")),"UTF8"));
// write in TREC format
out.write("...");
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
答案 0 :(得分:3)
烨。您正在泄漏文件描述符。
在第一种情况下,您打开数据库连接并且从不关闭它。连接通常使用套接字或其他东西与数据库通信。由于您没有关闭连接,因此不会关闭套接字,并且您将泄漏文件描述符。
在第二种情况下,我怀疑对 Jsoup.connect(urlVal)
的调用是打开一个连接,然后你不会关闭。这将导致文件描述符泄漏。
更正 - close()
界面上没有Connection
方法。看起来必须创建实际连接,然后通过get
方法在内部关闭。假设是这样,在第二种情况下没有文件描述符泄漏。
第三种情况不会泄漏文件描述符。但是,如果您无法打开文件,out.close();
语句将尝试在null
上调用方法...并将抛出一个NPE。
解决方案是找到所有打开文件,数据库连接,http连接的位置,并确保句柄始终关闭。
实现此目的的一种方法是将close()
调用(或等效内容)置于finally
块中...但请确保您不会在close()
上意外调用null
{1}}。
另一种方法是使用Java 7“try with resource”语法。例如:
private static void storeFile(String id, String url, String cat,
String title, String contents) {
try (BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(
new File(path + "/" + id + ".webtrec")),"UTF8"))) {
// write in TREC format
out.write("...");
out.close();
} catch (IOException e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
(但请注意,Java 7语法只能与实现新Closeable
接口的资源一起使用。)
答案 1 :(得分:2)
添加到Stephen的综合分析中。我建议为数据库使用连接池,但是,正如斯蒂芬指出的那样,除非你关闭这些连接,否则你会把游泳池关闭,但至少会更容易发现原因......
我没有看到任何证据,但您应该使用某种线程池来下载页面,这将有助于最大化系统资源。一些执行者服务就足够了。就像我说的那样,你可能已经这样做了,但你没有显示任何代码(或评论)。