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

C#中用List存储数据库打印数据引发内存不足的解决方案咨询

解决C#打印大量数据时的OutOfMemoryException问题

嘿,遇到内存不足的问题确实头疼,尤其是打印大量数据的时候。咱们来一步步拆解问题,找到合适的解决方案:

核心原因分析

你现在的做法是把所有数据库数据一次性加载到List<FormName>中,当数据量超过300行(可能每行包含较大字段,比如文本、图片等)时,内存里会堆积大量对象,最终触发OutOfMemoryException。解决思路的核心就是减少内存中同时存在的数据量。


方案1:流式打印(推荐,内存占用最低)

直接放弃用List缓存所有数据,改为边从数据库读取边打印,内存里始终只保留当前处理的一行数据,彻底避免内存堆积。

修改后的代码示例:

// 不需要全局的List<FormName>了
public string printItemCode;
public string printPartNo;
public string printIssued;
public string printUOM;

// 数据库读取+打印逻辑
using (var rdr = cmd.ExecuteReader())
{
    int currentLine = 0;
    // 绑定PrintDocument的PrintPage事件,在事件中处理逐行打印和分页
    PrintDocument pd = new PrintDocument();
    pd.PrintPage += (sender, e) =>
    {
        // 每次打印一页,直到数据读完
        while (rdr.Read() && currentLine < pd.DefaultPageSettings.PrintableArea.Height / 20) // 假设每行高度20
        {
            // 读取当前行数据
            printItemCode = rdr["ItemCode"].ToString();
            printPartNo = rdr["PartNo"].ToString();
            printIssued = rdr["Issued"].ToString();
            printUOM = rdr["UOM"].ToString();
            
            // 绘制当前行到打印页面
            e.Graphics.DrawString(printItemCode, new Font("Arial", 10), Brushes.Black, 10, currentLine * 20);
            e.Graphics.DrawString(printPartNo, new Font("Arial", 10), Brushes.Black, 100, currentLine * 20);
            // ...绘制其他字段
            
            currentLine++;
        }
        
        // 如果还有未读数据,触发下一页打印
        e.HasMorePages = rdr.HasRows;
        currentLine = 0; // 重置行号,准备下一页
    };
    
    // 开始打印
    pd.Print();
}

这种方式的优势是内存占用极低,不管多少行数据,内存里都只有当前行和打印页面的临时对象。


方案2:分批加载打印(适合需要预览或批量处理的场景)

如果必须要缓存部分数据(比如打印预览需要提前加载几页),可以分批读取数据,打印完一批就清理内存,再加载下一批。

代码示例:

int batchSize = 50; // 每次加载50行,可根据内存情况调整
List<FormName> currentBatch = new List<FormName>(batchSize);

using (var rdr = cmd.ExecuteReader())
{
    while (rdr.Read())
    {
        FormName item = new FormName();
        // 赋值属性
        item.ItemCode = rdr["ItemCode"].ToString();
        item.PartNo = rdr["PartNo"].ToString();
        item.Issued = rdr["Issued"].ToString();
        item.UOM = rdr["UOM"].ToString();
        
        currentBatch.Add(item);
        
        // 批次满了就打印并清理
        if (currentBatch.Count == batchSize)
        {
            PrintBatch(currentBatch);
            currentBatch.Clear(); // 清空批次,释放内存
            // 内存紧张时可以主动提示GC回收(非必须,GC会自动处理)
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
    
    // 打印剩余的不足一批的数据
    if (currentBatch.Count > 0)
    {
        PrintBatch(currentBatch);
        currentBatch.Clear();
    }
}

// 批量打印方法
private void PrintBatch(List<FormName> batch)
{
    PrintDocument pd = new PrintDocument();
    int currentLine = 0;
    pd.PrintPage += (sender, e) =>
    {
        foreach (var item in batch)
        {
            if (currentLine >= pd.DefaultPageSettings.PrintableArea.Height / 20)
            {
                e.HasMorePages = true;
                currentLine = 0;
                return;
            }
            
            // 绘制当前项到页面
            e.Graphics.DrawString(item.ItemCode, new Font("Arial", 10), Brushes.Black, 10, currentLine * 20);
            e.Graphics.DrawString(item.PartNo, new Font("Arial", 10), Brushes.Black, 100, currentLine * 20);
            // ...绘制其他字段
            
            currentLine++;
        }
        e.HasMorePages = false;
    };
    
    pd.Print();
}

额外优化建议

  1. 精简数据库查询:不要用SELECT *,只选择打印需要的列(比如ItemCode, PartNo, Issued, UOM),减少数据传输和内存占用。
  2. 优化FormName类:如果类中有大对象(比如byte[]存储图片、超长文本),而打印不需要这些字段,就不要加载它们,或者改为延迟加载。
  3. 检查打印资源释放:确保PrintDocument、Graphics、Font等对象都正确调用Dispose(),避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:11:25