【发布时间】:2018-02-16 10:09:45
【问题描述】:
我需要将大量数据(我可以在一秒钟内获得高达 1KB 的数据)写入多个不同的文件(每个文件最多可以增加几个 GiB 的空间。
在我的应用程序中,一个线程不断生成数据,目前我正在为每个文件创建一个线程来写入数据(创建一个新文件取决于生产者正在生成的输入数据的某些标准)。
我正在使用由 BufferedWriter 包装的 FileWriter。最初我尝试使用 8KB 的默认缓冲大小进行写入。但是由于这使得每秒的写入次数更高,所以 CPU 消耗会迅速增加而不会下降。
所以,我现在已将缓冲大小增加到 50KB。 但这使我的应用程序由于 outOfMemory 问题而崩溃。
当我对其进行分析时,我可以看到所有数据都以 char 数组的形式存储,该数组由缓冲写入器创建(记住每个文件一个缓冲写入器。我有大约 400 个这样的文件)。
请在此处提出如何解决该问题的建议。 我也想知道是否有任何替代方案或更好的方法来实现该要求。
我不能将缓冲区大小减少到 50KB 以下,因为这会使我的 CPU 达到 100%。
编辑:好的,我认为我不需要添加代码,因为我可以使要求非常清楚(而且我与代码无关。没关系,只是我在寻找效率)。但是,由于我的问题被否决了,所以我将我的代码包括在这里。
public class DataWriter {
private LinkedBlockingQueue<MyDataObject> dataQueue = new LinkedBlockingQueue<>();
private ExecutorService singleThread = Executors.newSingleThreadExecutor();
private boolean isRunning = true;
private Map<String, FileWriterThread> map = Collections.synchronizedMap(new HashMap<String, FileWriterThread>());
public DataWriter() {
singleThread.submit(new DataProcessor());
}
public void writeProducedData(MyDataObject object) {
if (isRunning) {
dataQueue.offer(object);
}
}
public void stopWriting() {
isRunning = false;
}
private class DataProcessor implements Runnable {
@Override
public void run() {
while (isRunning) {
MyDataObject obj = dataQueue.take();
if (obj.getMapKey() == null) {
FileWriterThread thread = new FileWriterThread();
map.put(obj.getMapKey(), thread);
}
FileWriterThread thread = map.get(obj.getMapKey());
thread.writeData(obj);
}
}
}
}
FileWriterThread 类:
public class FileWriterThread {
private ExecutorService singleThread = Executors.newSingleThreadExecutor();
private FileWriter fileWriter;
private BufferedWriter bufferedWriter;
private LinkedBlockingQueue<MyDataObject> dataQueue = new LinkedBlockingQueue<>();
public FileWriterThread() {
singleThread.submit(new DataProcessor());
}
public void writeData(MyDataObject obj) {
if (fileWriter == null) {
createWriter(obj.getFileName());
}
dataQueue.offer(obj);
}
public void stopWriting() {
// close the file writer and buffer writer gracefully
}
private void createWriter(String fileName) {
try {
fileWriter = new FileWriter(fileName, true);
bufferedWriter = new BufferedWriter(fileWriter, 50);
} catch (Exception e){}
}
private class DataProcessor implements Runnable {
@Override
public void run() {
MyDataObject obj = dataQueue.take();
try {
bufferedWriter.write(obj.toString());
} catch(Exception e) {}
}
}
}
【问题讨论】:
-
显示代码的相关部分。
-
如果您在单个磁盘上写入文件,则使用单个线程进行写入很可能会更快,并且比动态创建的拥有 400 个线程(这是巨大的)消耗的内存要少得多,并且每次写入一个单独的文件。当然,由于您没有发布任何一行代码,我们只能猜测。
-
您是否以 1MB/s 的速度写入每个文件?因为那将是 400 MB/s,在 HDD 上是不可能的。你的硬件平台是什么?
-
抱歉@AndrewHeys,已将其更正为 1 KB/s
-
@JBNizet,请查看我添加的代码。另外,如果我只使用一个线程写入 400 个文件,我正在写入的数据量仍然是一样的。你能告诉我这里的内存将如何减少吗?
标签: java java-io filewriter bufferedwriter