目的:
前几天在开发过程中遇到一个需求: 读取一个大约5G的csv文件内容,将其转化为对象然后存储到redis中, 想着直接开大内存直接load 进入到内存中就行了,结果可想而知,5G的文件 ,Xmx 开到10G都没有解决,直接out of Memory 异常
1、读入大文件
① 直接load 进入到内存中
这种处理大文件很容易造成 内存不够的问题
String path = "F:\\originData\\snapshotDepth\\2022-06-20\\sh_stock_now.csv";File testFile = new File(path);List<String> file = FileUtil.readLines(testFile, Charset.defaultCharset());
这种 500M的文件,堆内存一般会占用2.5G ,那么如果你去读一个5G的文件,那么内存直接起飞,所以这种不建议使用
② 按照行读取文件
String path = "F:\\originData\\snapshotDepth\\2022-06-20\\sh_stock_now.csv";Scanner scanner = null;try (FileInputStream fileInputStream = new FileInputStream(path)) {scanner = new Scanner(fileInputStream);while (scanner.hasNextLine()) {String line = scanner.nextLine();log.info("对应的行的信息---{}", line);}} catch (Exception ex) {log.error("捕获到异常---{}", ex.getMessage(), ex);}
这种因为是有按照一行行读取到内存当中, 所以耗时肯定增加
③ 使用common io 中的函数
public void testCommon() {String path = "F:\\originData\\snapshotDepth\\2022-06-20\\sh_stock_now.csv";File file = new File(path);try {Integer count = 0;Long startTime = System.currentTimeMillis();final LineIterator lineIterator = FileUtils.lineIterator(file, "utf-8");while (lineIterator.hasNext()) {count++;String line = lineIterator.nextLine();if (count % 1000000 == 0) {Long stepTime = System.currentTimeMillis();log.info("读取100w 数据耗时为---{}", stepTime - startTime);startTime = stepTime;}}} catch (Exception ex) {log.error("读取文件异常---{}", ex.getMessage(), ex);}}
运行结果:
每100w行的数据 大概耗时为1.5秒, 这个是在我的笔记本上运行的结果,如果在性能更好的机器上速度应该更快。
2、写入文件
log.info("开始对FileWriter 写入文件进行计时");Long startTime = System.currentTimeMillis();String path = "F:\\originData\\test.txt";String content = "12312313";for (int i = 0; i < 100000; i++) {try (FileWriter fileWriter = new FileWriter(path, true)) {fileWriter.append(content);} catch (Exception ex) {log.error("捕获到异常---{}", ex.getMessage(), ex);}}Long stepEnd = System.currentTimeMillis();log.info("FileWriter 耗时为----{}", stepEnd - startTime);String path2 = "F:\\originData\\test2.txt";log.info("开始对 BufferedWriter 写入文件进行计时");for (int i = 0; i < 100000; i++) {try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path2, true))) {bufferedWriter.append(content);} catch (Exception ex) {log.error("捕获到异常---{}", ex.getMessage(), ex);}}Long stepNextEnd = System.currentTimeMillis();log.info("BufferedWriter 耗时为----{}", stepNextEnd - stepEnd);log.info("开始对 BufferedOutputStream 写入文件进行计时");String path3 = "F:\\originData\\test3.txt";for (int i = 0; i < 100000; i++) {try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(path3, true))) {bufferedOutputStream.write(content.getBytes());} catch (Exception ex) {log.error("捕获到异常---{}", ex.getMessage(), ex);}}Long stepNextStream = System.currentTimeMillis();log.info("BufferedWriter 耗时为----{}", stepNextStream - stepNextEnd);String path4 = "F:\\originData\\test4.txt";File file = new File(path4);try {if (!file.exists()) {file.createNewFile();}for (int i = 0; i < 100000; i++) {FileUtils.write(file, content, true);}log.info("FileUtils 耗时为----{}", System.currentTimeMillis() - stepNextStream);} catch (Exception ex) {log.error("捕获到异常---{}", ex.getMessage(), ex);}
运行结果
如果是写入字符串的话 还是建议使用BuffereOutputStream