我有一个 Java 程序,必须执行以下 3 件事:
- 从网站下载文件。
- 通过 testA 和 testB 运行文件(均使用 Java)
- 删除该文件并将测试结果保存在磁盘上。
大约有 1,000,000 个不同的网站都已完成此操作。这应该是一个相当简单的任务,因为我只是从其他程序中粘贴了部分内容:testA
并且testB
这两个程序都已分别对数千万个不同的页面执行,没有任何问题,下载页面的例程也已对大约一百万个页面执行过几次,也从未出现任何问题。它们都在 Ubuntu 10.4 机器上执行。
但是,当依次执行这 3 项操作时,无论文件写入哪个磁盘都会崩溃。我第一次在外部 USB HD 上运行它时,必须手动断开连接并重新连接才能恢复运行(否则 Linux 无法识别它)。下一次,在内部 HD 上运行它时,整个系统都崩溃了,我不得不手动重新启动它。写入 Ram Disk 时也发生了同样的情况。
问题是我无法真正找出问题所在。崩溃发生的时间太长了(大约 50 小时左右,但随机性很强),因此测试时间太长,而且没有系统日志表明故障发生的位置/原因。机器或 HD 只是停止响应。
除了崩溃之外,一切都运行正常。文件创建和删除正常,线程不会死亡并正常执行,两个测试都运行正常。更改内存或线程数对锁定时间没有影响。我已经检查了套接字或类似的东西是否未关闭,但我甚至不知道如何开始测试,我不知道使用 Java 会导致系统崩溃得如此严重。
编辑:我所说的挂断是指,当我在外部 HD 上运行它时,Linux 无法识别该 HD,而当我在内部 HD 或 Ram Disk 上运行它时,计算机不会响应任何 I/O,没有任何内容写入磁盘,cactii 日志未被记录等。例如,无法使用 SSH 连接。
程序运行方式的示例:
List<String> pagesToDownload = getFromDataBase();
for(i=0;i<NumThreads;i++){
launchTestThread();
}
然后,在每个线程上:
String pageName = getNextPageToDownload();
File downloadedFile = downloadPage(pageName);
TestAResults testAResults = runTestA(downloadedFile);
TestBResults testBResults = runTestB(downloadedFile);
writeToDatabase(downloadedFile, testAResults, testBResults);
downloadedFile.delete();
单独的函数runTestA
、runTestB
和downloadedPage
可以处理更大量的文件,但是当以这种方式调用时,它们却不起作用。而且是在同一个硬件上。
编辑2:我想我排除了硬件问题。一款同样对硬件要求很高的软件在过去 7 天内一直在同一台机器上运行,没有任何问题。无论如何,只要我能得到一台闲置的机器,我就会在其中测试该程序。
此外,测试中的所有内容都会写入数据库,直到发生崩溃为止,并且数据是正确的。downloadedFile
在 writeToDatabase 方法期间不会作为参数传递,只有其名称和大小。
最后,我进行了一些广泛的内存或文件处理程序泄漏检查,但一无所获,包括在工作测试中。目前,我认为文件删除存在一些奇怪的错误。
编辑3:我终于设法找到了另一台机器来测试该例程。另一台硬件,但 Ubuntu 版本相同(10.4 LTS)。它也在那里崩溃了,所以我真的怀疑这是硬件问题。这要么是操作系统错误,要么是 JVM 错误,要么是编程错误(没有 JNI 或类似的东西在运行)。我将尝试在其他环境中运行测试(在 FreeBSD 中设置测试非常容易,我可以尝试找到一台 Windows 机器来测试它)以验证这一点。
编辑4:回答 Bob Cross 关于文件有多大的问题,它们是典型的网页,平均大小约为 20kb。我必须删除它们,因为这样做的目的是扩展应用程序,使磁盘使用率难以忍受。但我将尽快尝试无删除运行。我运行这些测试的机器现在正在使用,我很难获得一些空闲的硬件。
答案1
如果系统停止响应,则可能是操作系统中的错误或硬件问题。无论错误有多少,程序都不应该挂起系统。
在不同的系统上运行你的程序,看看它是否为你的程序提供了有用的诊断,而不仅仅是翻倒。
答案2
有点难以确切地看出您对上述摘要做了什么,但我会尝试根据这一段做出猜测:
但是,当依次执行这 3 项操作时,无论文件写入哪个磁盘都会崩溃。我第一次在外部 USB HD 上运行它时,必须手动断开连接并重新连接才能恢复运行(否则 Linux 无法识别它)。下一次,在内部 HD 上运行它时,整个系统都崩溃了,我不得不手动重新启动它。写入 Ram Disk 时也发生了同样的情况。
您如何管理与您正在检查的百万个站点列表相关的结果文件?具体来说,您的代码是否如下所示(抽象的伪代码如下):
- 打开结果文件。
- 循环开始:针对一百万个站点中的每一个
- 现场测试A
- 现场测试B:循环结束
- 将站点测试结果写入结果文件。
如果是这样,我怀疑你遇到了一个问题,因为你正在慢慢积累尚未写入结果文件的结果。我怀疑它们正位于多个缓存中的一个中,等待机会将这些更改刷新到磁盘。
如果你正在做上述操作,请尝试以下操作:
- 循环开始:针对一百万个站点中的每一个
- 现场测试A。
- 现场测试B。
- 打开结果文件。
- 将站点测试结果写入结果文件。
- 关闭结果文件:循环结束
这应该确保每次测试后,你都会将结果写入文件。至少,你应该能够观察程序的进程。
编辑:跟进问题的编辑:
writeToDatabase(downloadedFile, testAResults, testBResults);
两个问题:
您的数据库是否曾被写入过更改?比如,它们写入一段时间后就停止了?还是数据库完全是空的?
代码真的是这样写的吗?如果是这样,则存在潜在的内存泄漏:File 引用被传递到您的
writeToDatabase
方法中。您确定没有任何东西挂在该引用上吗?
有可能由于文件引用和/或本机文件句柄泄漏,导致您持有过多的文件句柄。值得检查。
再次编辑根据更多反馈:
现在,我的钱花在了文件删除的一些奇怪的错误上。
这是有可能的。以下是一些需要考虑的事情:
您要下载的文件有多大?您可以尝试在不删除任何文件的情况下进行调试运行吗?如果您在没有该代码的情况下运行更长时间,那肯定会是一个有趣的结果。
我仍然担心您正在泄露文件句柄。如果您检查已处理的文件数并发现一些可疑的文件数(例如 1024 的倍数),我会更仔细地查看 delete 方法中的内容。当然,如果没有实现,很难判断。
答案3
没有任何进一步的信息,我猜你的磁盘已经满了。无法通过 SSH 连接有点奇怪,但如果你无法写入,这是可能的auth.log
。
答案4
...没有具体信息很难说。
文件写入后,IO 流是否全部关闭?您是否在 unix 上设置了一些 IO 限制?由于它们同时修改文件,是否存在并发问题?也许您还可以偶尔进行几次 Thread.sleep(...) 以避免磁盘饱和。
......但实际上,我不知道,这只是我的意见。