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

SDL2程序触发OOM被杀 帧循环重复加载图片问题排查修复

SDL2 2D程序运行时内存持续上涨触发OOM问题

问题复现代码

#include"global.h"
#include"tools/entity.h"
#include"tools/input.c"
static const int width = 800;
static const int height = 600;
int main (int argc, char **argv)
{
    SDL_Init(SDL_INIT_VIDEO);
    int init = IMG_Init(IMG_INIT_PNG);
    if((init & IMG_INIT_PNG) != IMG_INIT_PNG) {
        printf("Couldnt load images :P");
    }   
    // 创建窗口
    SDL_Window *window = SDL_CreateWindow("Ferengine",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,width,height,SDL_WINDOW_SHOWN);
    // 创建渲染器
    SDL_Renderer *renderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);    
//  SDL_Surface * image = IMG_Load("funnyman.png");
//  if(!image) { printf("Could not load image because : %s\n",IMG_GetError()); }
//  SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);
    // 渲染设置
/*  SDL_SetRenderDrawColor(renderer,255,0,0,255);
    SDL_SetWindowOpacity(window,1.0f);
*/
    
    entity2D player;
    player.posx = 0;
    player.posy = 0;
    player.sprite = "funnyman.png";
    int speed = 100;
    // 主循环标记
    bool running = true;
    SDL_Event event;
    while(running) 
    {
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT)
            {
                running = false;
            }
        }   
    SDL_RenderClear(renderer);
//  SDL_RenderCopy(renderer, texture, NULL, NULL);
    drawImage("funnyman.png",renderer,10,10,100,100);
    drawEntity(player,renderer,100,100);
    switch(getKeyPressed()) {
        case SDLK_w:
            player.posy += 1 * speed;
            break;
        case SDLK_s:
            player.posy -= 1 * speed;
            break;
        case SDLK_d:
            player.posx += 1 * speed;
            break;
        case SDLK_a:
            player.posx -= 1 * speed;
            break;
        case SDLK_q:
            running = false;
        default:
            break;
    }
    SDL_RenderPresent(renderer);

    }
    // 释放资源
    IMG_Quit();
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

说明:代码中getKeyPressed()为自定义封装函数,返回int类型(SDL_Keycode底层本质为int类型),程序使用的编译命令为gcc -o Build/Ferengine Source/main.c -lSDL2 -lSDL2_image -ldl

问题现象

程序启动后角色移动逻辑可正常运行,但静置一段时间后窗口会自动关闭,触发*OOM(Out of Memory,内存不足)*错误,进程被系统强制杀死。

根因分析

问题核心出在渲染阶段的资源加载逻辑:

  • 主循环每帧执行时都会调用drawImage和drawEntity两个绘制函数,两个函数内部每次调用都会重新从磁盘加载图片文件、创建对应的SDL_Surface和SDL_Texture资源
  • 每次创建的临时纹理、表面资源使用完成后没有调用对应销毁接口释放,造成持续性内存泄漏
  • 程序开启了垂直同步,会以匹配显示器刷新率的频率(通常每秒60次)重复加载资源、泄漏内存,即使用户不做任何操作,内存占用也会持续上涨,最终耗尽系统可用资源触发OOM。

修复方案

  • 所有需要重复使用的图片、纹理资源,统一在进入主循环前完成预加载,将生成的SDL_Texture*缓存起来,不要在渲染循环内做磁盘IO和重复的资源创建操作
  • 渲染阶段直接读取预加载好的纹理资源,调用SDL_RenderCopy完成绘制,避免无意义的资源开销
  • 程序退出阶段,先销毁所有预加载的纹理资源,再按顺序销毁渲染器、窗口,最后退出SDL子系统,避免退出阶段的资源泄漏。

内容的提问来源于stack exchange,提问作者wij

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:33:34