You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 20:42:16