使用IntelliJ与Kotlin读取大型CSV文件时遭遇Java堆内存溢出问题求助
解决Kotlin读取大CSV文件的堆内存溢出问题
嘿,我完全懂你现在的困扰——直接把350万条Product记录全塞进ArrayList里,就算把堆内存调到10G也可能撑不住,这根本不是堆大小的问题,而是数据处理方式的问题。
问题根源
你当前的代码把所有解析后的Product对象都存在一个MutableList里,这意味着程序运行时,350万个对象会同时占用内存。就算每个Product只占100字节(实际远不止),350万条也得350MB,再加上CSV解析过程中的临时对象、JVM本身的内存开销,很容易触发OutOfMemoryError。调堆内存只是治标,而且文件再大一点(比如1亿条),堆再大也顶不住。
正确的解决方案:流式/分批处理
核心思路是不要一次性加载所有数据到内存,而是逐行读取、处理,处理完就释放内存;或者分批读取,处理一批清空一批。下面给你两种具体实现方案:
方案1:流式处理(推荐)
用Kotlin的kotlinx-csv库(轻量高效)逐行解析CSV,每解析一条就直接处理(比如写入数据库、计算统计指标),不存储到List里。这样内存里永远只有当前行的Product对象,内存占用极低。
首先添加依赖(Gradle为例):
implementation "org.jetbrains.kotlinx:kotlinx-csv-jvm:0.10.1"
然后修改你的代码:
import kotlinx.csv.CsvReader import java.io.File data class Product( val year: Int, val month: Int, val cod_product: Int, val unit: Int, val country: Int, val uf: String, val transport: Int, val revenue: Int, val quantity: Int, val weight: Int, val price: Int ) fun main() { val csvFilePath = "你的大CSV文件路径.csv" // 用use()自动关闭流,避免资源泄漏 File(csvFilePath).bufferedReader().use { fileReader -> CsvReader(fileReader).use { csvReader -> // 如果CSV有表头,先跳过一行 csvReader.readNext() // 逐行解析并处理 for (row in csvReader) { val product = parseRowToProduct(row) // 这里替换成你的实际业务逻辑:比如写入DB、统计等 processSingleProduct(product) } } } } // 把CSV行解析为Product对象 private fun parseRowToProduct(row: List<String>): Product { return Product( year = row[0].toInt(), month = row[1].toInt(), cod_product = row[2].toInt(), unit = row[3].toInt(), country = row[4].toInt(), uf = row[5], transport = row[6].toInt(), revenue = row[7].toInt(), quantity = row[8].toInt(), weight = row[9].toInt(), price = row[10].toInt() ) } // 处理单个Product的业务逻辑 private fun processSingleProduct(product: Product) { // 示例:打印或者写入数据库 // println("处理产品:${product.cod_product}") // productDao.insert(product) }
方案2:分批处理(如果需要批量操作)
如果你的业务必须批量处理(比如批量插入数据库),可以每次读取固定数量的记录,处理完就清空List,再读取下一批:
fun main() { val csvFilePath = "你的大CSV文件路径.csv" val batchSize = 10000 // 每批处理1万条,可根据内存情况调整 File(csvFilePath).bufferedReader().use { fileReader -> CsvReader(fileReader).use { csvReader -> csvReader.readNext() // 跳表头 val batch = mutableListOf<Product>() for (row in csvReader) { val product = parseRowToProduct(row) batch.add(product) // 达到批大小就处理,然后清空 if (batch.size >= batchSize) { processProductBatch(batch) batch.clear() } } // 处理最后一批不足batchSize的数据 if (batch.isNotEmpty()) { processProductBatch(batch) } } } } // 批量处理Product的业务逻辑 private fun processProductBatch(batch: List<Product>) { // 示例:批量插入数据库 // productDao.insertBatch(batch) println("处理了${batch.size}条记录") }
额外小贴士
- 避免使用
ArrayList存储全量数据:除非你的内存真的能装下所有数据(但大文件场景下不推荐)。 - 尽量用原始类型:比如
Int而不是Integer(Kotlin默认就是原始类型的包装,但解析时注意避免不必要的装箱)。 - 关闭不必要的调试工具:比如IntelliJ的内存快照、调试器,这些会额外占用内存。
内容的提问来源于stack exchange,提问作者Burzi
相关产品推荐
相关产品推荐

