如何解决Pygame程序运行时持续占用内存的问题?
Pygame雨滴程序内存泄漏问题排查
我正在开发一个包含背景图、下雨视觉效果和音效的简单程序,用Sprite类实现雨滴和雨水溅落效果。运行时通过活动监视器观察到,Python初始占用约90MB内存,几分钟后飙升至500MB以上。
原本以为是未正确删除屏幕外的精灵(雨滴和水花),但用print检查精灵组长度后,发现数量并未堆积。排查后怀疑问题出在delete_dissapeared_raindrops()函数,但找不到具体原因。
以下是复现代码(需为Raindrop类加载对应图片,通过系统活动监视器观察内存占用):
import sys from random import randint import pygame from pygame.sprite import Group from pygame.sprite import Sprite # class class Raindrop(Sprite): def __init__(self): """initiate raindrop""" super().__init__() self.image = pygame.image.load('image/raindrop.bmp').convert_alpha() y_pos = randint(-screen_height, 0) self.rect = self.image.get_rect(centery = y_pos) def update(self): """let the raindrop drops from top to bottom""" self.rect.y += 8 def check_edge(self): """check if raindrop reaches the bottom of the screen""" if self.rect.top >= screen_height: return True # functions def update_screen(): screen.fill('black') rain.draw(screen) pygame.display.flip() # functions def get_available_raindrops_x(raindrop_width): """calculate how many raindrops available horizontally""" available_space = screen_width - (2 * raindrop_width) available_raindrops = int(available_space / (2 * raindrop_width)) return available_raindrops def create_rain(): if len(rain) <= 58: raindrop = Raindrop() available_raindrops = get_available_raindrops_x(raindrop.rect.width) for i in range(available_raindrops): raindrop = Raindrop() # raindrops are placed away from each other horizontally raindrop.rect.x += raindrop.rect.width + (raindrop.rect.width * 2) * i rain.add(raindrop) def delete_dissapeared_raindrops(): """delete the raindrops that crossed the bottom of the screen""" for raindrop in rain.copy(): if raindrop.check_edge(): rain.remove(raindrop) def update_screen(): screen.fill('black') rain.draw(screen) pygame.display.flip() # settings screen_width = 1200 screen_height = 820 # initialization pygame.init() clock = pygame.time.Clock() fps = 120 screen = pygame.display.set_mode((screen_width, screen_height)) # initializing sprite group rain = Group() # main program loop while True: clock.tick(fps) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() # creating and deleting rain print(len(rain)) create_rain() rain.update() delete_dissapeared_raindrops() update_screen()
问题根源
内存泄漏的核心原因不是精灵清理逻辑,而是:
- 每次创建
Raindrop实例时,都会调用pygame.image.load()重新加载图片。即使是同一张图片,每次加载都会生成新的Surface对象,这些对象不会被立即回收,持续占用内存。 create_rain函数中每次判断精灵组数量不足时,都会重复计算可用雨滴数量,还会额外创建一个无意义的Raindrop实例,进一步加剧重复加载。
修复方案
1. 全局加载雨滴图片,避免重复IO
把图片加载移到Raindrop类定义外,作为类属性只加载一次:
# 在Raindrop类定义前加载图片 RAIN_DROP_IMAGE = pygame.image.load('image/raindrop.bmp').convert_alpha() class Raindrop(Sprite): def __init__(self): super().__init__() self.image = RAIN_DROP_IMAGE # 复用已加载的图片对象 # ... 其他代码不变
2. 优化create_rain函数,减少冗余操作
提前计算好横向可容纳的雨滴数量,避免每次创建时重复计算:
# 在初始化部分提前计算可用雨滴数量 # 先创建一个临时雨滴获取尺寸 temp_drop = Raindrop() MAX_RAINDROPS_X = get_available_raindrops_x(temp_drop.rect.width) del temp_drop # 临时对象用完销毁 def create_rain(): if len(rain) <= 58: for i in range(MAX_RAINDROPS_X): raindrop = Raindrop() raindrop.rect.x = raindrop.rect.width + (raindrop.rect.width * 2) * i rain.add(raindrop)
3. 可选:优化精灵清理逻辑
虽然原清理逻辑没问题,但可以改用Sprite组的remove配合列表推导式,写法更简洁:
def delete_dissapeared_raindrops(): rain.remove(*[drop for drop in rain if drop.check_edge()])
修改后的完整代码
import sys from random import randint import pygame from pygame.sprite import Group from pygame.sprite import Sprite # 全局加载雨滴图片,仅加载一次 RAIN_DROP_IMAGE = pygame.image.load('image/raindrop.bmp').convert_alpha() # class class Raindrop(Sprite): def __init__(self): """initiate raindrop""" super().__init__() self.image = RAIN_DROP_IMAGE y_pos = randint(-screen_height, 0) self.rect = self.image.get_rect(centery=y_pos) def update(self): """let the raindrop drops from top to bottom""" self.rect.y += 8 def check_edge(self): """check if raindrop reaches the bottom of the screen""" return self.rect.top >= screen_height # functions def update_screen(): screen.fill('black') rain.draw(screen) pygame.display.flip() def get_available_raindrops_x(raindrop_width): """calculate how many raindrops available horizontally""" available_space = screen_width - (2 * raindrop_width) return int(available_space / (2 * raindrop_width)) def delete_dissapeared_raindrops(): """delete the raindrops that crossed the bottom of the screen""" rain.remove(*[drop for drop in rain if drop.check_edge()]) # settings screen_width = 1200 screen_height = 820 # initialization pygame.init() clock = pygame.time.Clock() fps = 120 screen = pygame.display.set_mode((screen_width, screen_height)) # 提前计算横向最大雨滴数量 temp_drop = Raindrop() MAX_RAINDROPS_X = get_available_raindrops_x(temp_drop.rect.width) del temp_drop # initializing sprite group rain = Group() # main program loop while True: clock.tick(fps) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() # creating and deleting rain print(len(rain)) if len(rain) <= 58: for i in range(MAX_RAINDROPS_X): raindrop = Raindrop() raindrop.rect.x = raindrop.rect.width + (raindrop.rect.width * 2) * i rain.add(raindrop) rain.update() delete_dissapeared_raindrops() update_screen()
内容的提问来源于stack exchange,提问作者Jaden
相关产品推荐
相关产品推荐

