Spark代码运行时Python内核因内存问题崩溃,原因何在?
Spark循环运行内存崩溃问题排查与解决
代码与运行方式
自定义函数
import pandas as pd import pyspark.sql.functions as F import numpy as np def my_func(): df = pd.DataFrame( np.random.randn(1000000, 100) ) y = spark.createDataFrame(dd) # 10 X 1k(注:此处dd未定义,疑为笔误) y2 = y.select(F.sum(F.col("1"))) y3 = y2.collect() y2.unpersist(blocking=True) y2 = None return y3
循环执行代码
list_of_results = [] for i in range(100): list_of_results.append(my_func())
错误信息
Fatal error: The Python kernel is unresponsive.
The Python process exited with an unknown exit code.
环境与内存情况
- 集群总可用内存28GB(2个worker节点,单节点分配14GB)
- 崩溃前内存使用符合预期,未出现持续暴涨的情况
问题原因分析
- Python端内存泄漏:每次迭代生成的100万行×100列的Pandas DataFrame(约760MB),理论上函数结束后会被GC回收,但Python GC存在延迟,加上循环中可能有隐式引用残留,导致内存逐步累积,最终撑爆Python进程。
- 变量定义错误:代码中
spark.createDataFrame(dd)的dd未定义,若实际运行中是动态生成的对象,可能存在引用未及时释放的问题。 - 结果列表内存累积:
list_of_results持续收集每次collect()的结果,随着迭代次数增加,内存占用不断升高。 - Py4J桥接对象引用残留:Spark对象(如DataFrame)通过Py4J与JVM交互,即使调用
unpersist,Python端的桥接对象可能仍持有引用,导致JVM资源无法及时释放,间接影响Python进程内存。
解决方案
1. 显式清理大对象并触发GC
在函数内显式删除大对象,强制触发Python垃圾回收,减少内存残留:
import gc def my_func(): df = pd.DataFrame(np.random.randn(1000000, 100)) y = spark.createDataFrame(df) # 修正dd为df的笔误 y2 = y.select(F.sum(F.col("1"))) y3 = y2.collect() # 清理Spark对象 y2.unpersist(blocking=True) del y, y2 # 清理Pandas大对象并触发GC del df gc.collect() return y3
2. 避免结果全量存储
如果不需要保留所有迭代结果,改用生成器按需处理,减少内存占用:
def generate_results(): for i in range(100): yield my_func() # 逐个处理结果,不存储全部 for res in generate_results(): # 此处添加结果处理逻辑 pass
3. 优化Spark操作模式
将循环内的重复创建DataFrame操作改为批量分布式计算,减少Python与JVM的交互开销:
# 一次性生成全量数据并转为Spark DataFrame total_df = pd.DataFrame(np.random.randn(1000000 * 100, 100)) spark_total_df = spark.createDataFrame(total_df) # 直接在Spark上完成聚合,避免循环collect result = spark_total_df.select(F.sum(F.col("1"))).collect()
4. 调整Python进程内存配额
若使用Notebook环境,增加Python内核的内存限制,避免因内存阈值过低导致内核崩溃。
5. 检查并清理Py4J引用
确保所有Spark相关对象在使用后被显式删除,避免Py4J桥接对象持有JVM资源,可通过del关键字删除所有Spark对象后触发GC。
内容的提问来源于stack exchange,提问作者ThomasWear
相关产品推荐
相关产品推荐

