Java应用抛出Too many open files异常,求排查解决
让我梳理下你的问题:你用了try-with-resources来处理文件写入,理论上应该自动关闭流,但还是触发了java.io.FileNotFoundException: /file/path (Too many open files),而且调整文件写入逻辑(替换流、创建新实例)都没用,处理恰好5016个文件后就出问题。结合你提供的API代码,问题根本不在文件写入部分,而是HTTP请求的资源泄漏导致的文件句柄耗尽。
问题根源分析
1. 未处理HTTP错误响应的错误流
当HttpURLConnection返回非200的状态码时,系统会打开一个错误流(connection.getErrorStream())来返回错误信息。如果你不读取并关闭这个流,对应的连接资源(包括底层的文件句柄)不会被释放,随着请求次数增加,句柄会被耗尽。你之前的代码只在响应成功时处理了输入流,错误情况完全忽略了这一点。
2. 多余的手动close()调用(虽不直接导致泄漏,但属于不良实践)
在handleResponse的try-with-resources块里,你手动调用了reader.close()和in.close()——这完全没必要,try-with-resources会自动关闭实现了AutoCloseable的资源,重复关闭可能引发不必要的异常,干扰资源释放逻辑。
修复方案
修改你的handleResponse方法,确保无论请求成功还是失败,都正确处理所有流资源:
private static CoreEntity[] handleResponse(CoreEntity[] resultList, HttpURLConnection connection) throws IOException { final int status = connection.getResponseCode(); InputStream inputStream = null; try { // 根据响应状态获取对应的输入流(成功流或错误流) inputStream = (status == HttpURLConnection.HTTP_OK) ? connection.getInputStream() : connection.getErrorStream(); if (inputStream != null) { try (InputStreamReader reader = new InputStreamReader(inputStream); BufferedReader in = new BufferedReader(reader)) { String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } if (status == HttpURLConnection.HTTP_OK) { // 成功处理逻辑 JSONArray jsonArray = getJSONAsArray(response.toString()); resultList = (CoreEntity[]) Array.newInstance(resultList.getClass().getComponentType(), jsonArray.length()); for (int i = 0; i < jsonArray.length(); i++) resultList[i] = (CoreEntity) GSON.fromJson(jsonArray.get(i).toString(), resultList.getClass().getComponentType()); } else { // 可选:打印错误响应内容,方便调试 System.out.println("Request failed with code " + status + ", response: " + response.toString()); } } catch (JSONException e) { e.printStackTrace(); } } else { System.out.println("Request failed with error code: " + status + ", no response stream available"); } } finally { // 确保输入流被彻底关闭 if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } // 断开连接,释放底层资源 connection.disconnect(); } return resultList; }
额外验证步骤
- 可以用系统工具排查进程的打开文件句柄:Linux下用
lsof -p <你的进程ID>,Windows下用Process Explorer,看看修复后句柄数量是否不再持续增长 - 检查
getJSONAsArray方法,确保它没有打开额外的资源(比如文件、流)却未关闭
为什么之前的文件写入调整无效?
因为你的文件写入代码本身是正确的——try-with-resources已经自动关闭了FileOutputStream,你替换流、创建新DataFileWriter实例的操作都是针对没有问题的部分,自然无法解决HTTP请求导致的句柄泄漏。
内容的提问来源于stack exchange,提问作者user1830414

