【问题标题】:Memory leaks looping through images to byte arrays通过图像循环到字节数组的内存泄漏
【发布时间】:2014-10-04 00:57:52
【问题描述】:

我有一个例程扫描文件夹中的图像,然后为每个图像,将其转换为缩略图,然后将该缩略图上传到数据库。仅当数据库中该特定文件夹没有任何项目时,它才会运行。我的问题是我在处理几个文件夹后收到内存不足异常,我不确定泄漏发生在哪里。我已经尝试在循环中处理所有我可以丢弃的东西,但显然有些东西仍然从裂缝中掉下来。

private bool LoadImages(int folderid, int parentid) {
  ProgressScreen tfrm = new ProgressScreen();
  tfrm.Hide();
  DataTable mtable = new DataTable();
  List<FileInfo> lfile;
  mtable = Requests.ProcessSQLCommand(sqlconn, "Select f.ID, f.FolderName,f.FolderPath,f.ParentID,f.Root from Folders f where f.ID = " + folderid);
  DataTable ptable = Requests.ProcessSQLCommand(sqlconn, "Select Root from Folders where ID = " + parentid);
  if (ptable != null && ptable.Rows.Count > 0) {
    if (ptable.Rows[0]["Root"].ToString().ToLower() == "true") {
      return false;
    }
  }
  bool process = true;
  DirectoryInfo di = new DirectoryInfo(mtable.Rows[0]["FolderPath"].ToString());
  FileInfo[] smFiles = di.GetFiles("*.jpg", SearchOption.TopDirectoryOnly);
  lfile = smFiles.ToList<FileInfo>();
  if (lfile.Count <= 0) {
    process = false;
  }
  if (process) {
    tfrm.Show(this);
    for (int c = 0; c < lfile.Count; c++) {
      if (((FileInfo)lfile[c]).Extension == ".txt") {
        lfile.RemoveAt(c);
      }
      if (((FileInfo)lfile[c]).FullName.ToLower().Contains("cover")) {
        lfile.RemoveAt(c);
      }
    }
      for (int b = 0; b < lfile.Count; b++) {
        Cursor.Current = Cursors.WaitCursor;
        this.Enabled = false;
        try {
          tfrm.Location = new Point((Screen.PrimaryScreen.WorkingArea.Width / 2) - (tfrm.Width / 2), (Screen.PrimaryScreen.WorkingArea.Height / 2) - (tfrm.Height / 2));
        } catch {
        }
        tfrm.SetProgress((int)(((double)(b + 1) / (double)lfile.Count) * 100), "Loading Images", lfile[b].Name.ToString());
        tfrm.Refresh();
        int recid = 0;
        DataTable ttable = Requests.ProcessSQLCommand(sqlconn, "Insert into Image (Name,FolderID,ParentID) VALUES ('" + lfile[b].Name + "'," + folderid + "," + parentid + ") Select SCOPE_IDENTITY()");
        if (ttable != null && ttable.Rows.Count > 0) {
          recid = int.Parse(ttable.Rows[0][0].ToString());
        }
        if (recid > 0) {
          Image timg = null;
          byte[] traw = new byte[0];
          traw = File.ReadAllBytes(lfile[b].FullName);
          MemoryStream tstream = new MemoryStream(traw);
          timg = System.Drawing.Image.FromStream(tstream);
          tstream.Dispose();
          timg = Requests.FixedSize(timg, 600, 600);
          tstream = new MemoryStream();
          timg.Save(tstream, System.Drawing.Imaging.ImageFormat.Png);
          timg.Dispose();
          traw = new byte[0];
          traw = tstream.ToArray();
          tstream.Dispose();
          System.Data.SqlClient.SqlConnection con = new System.Data.SqlClient.SqlConnection(sqlconn);
          con.Open();
          System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand("Update Image set [Thumb] = Convert(VarBinary(MAX),@Image) where ID = " + recid, con);
          cmd.Parameters.AddWithValue("@Image", traw);
          cmd.ExecuteNonQuery();
          con.Dispose();
        }
      }
    this.Enabled = true;
    Cursor.Current = Cursors.Default;
    tfrm.Close();
    tfrm.Dispose();
    System.Windows.Forms.Application.UseWaitCursor = false;
    return true;
  } else {
    return false;
  }
}

固定尺寸​​法:

public static Image FixedSize(Image imgPhoto, int Width, int Height) {
  int sourceWidth = imgPhoto.Width;
  int sourceHeight = imgPhoto.Height;
  int sourceX = 0;
  int sourceY = 0;
  int destX = 0;
  int destY = 0;

  float nPercent = 0;
  float nPercentW = 0;
  float nPercentH = 0;

  nPercentW = ((float)Width / (float)sourceWidth);
  nPercentH = ((float)Height / (float)sourceHeight);
  if (nPercentH < nPercentW) {
    nPercent = nPercentH;
    destX = System.Convert.ToInt16((Width - (sourceWidth * nPercent)) / 2);
  } else {
    nPercent = nPercentW;
    destY = System.Convert.ToInt16((Height - (sourceHeight * nPercent)) / 2);
  }

  int destWidth = (int)(sourceWidth * nPercent);
  int destHeight = (int)(sourceHeight * nPercent);

  Bitmap bmPhoto = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
  bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution);

  Graphics grPhoto = Graphics.FromImage(bmPhoto);
  grPhoto.Clear(Color.Magenta);
  grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
  grPhoto.DrawImage(imgPhoto, new Rectangle(destX, destY, destWidth, destHeight), new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), GraphicsUnit.Pixel);

  grPhoto.Dispose();
  return bmPhoto;
}

原始图像(固定大小之前)的范围从 2MB 到 20MB,表中返回的图像 (max(len(image)) 约为 750 KB。(编辑:更正的文件大小)

每个文件夹的图像数量 ~100-150

我已经查看和调试过,但找不到导致 OOM 问题的原因,有人可以指出错误,或者为我正在做的事情提供更好的优化吗?

【问题讨论】:

  • 你可能不是内存不足,而是资源不足。那里有很多东西应该被处理,但可能不会。你可以通过任务管理器查看 GDI 对象和句柄。
  • FWIW,使用the using statement 将大大清理代码
  • 您拥有的最大图像的尺寸是多少?您是否以 32 位运行此代码?它总是在特定图像上爆炸吗?
  • 正如@VikasGupta 指出的那样,由于未压缩图像的大小,100MB JPG 几乎肯定会导致 OOM(甚至可能在 x64 上)。
  • @VikasGupta 最大图像为 18.3MB 3744x6516x24b 程序编译为 64 位。不,它不是一个特定的图像,通常是在处理大约 20 到 30 个文件夹之后。

标签: c# image memory-leaks bytearray


【解决方案1】:

如果这是一次代码审查,我会提出很多改进建议... 但是无论如何,特别是针对您的问题..您的内存泄漏在于以下代码行(请不要个人认为,但很难发现,因为代码不干净)

      timg = System.Drawing.Image.FromStream(tstream);
      tstream.Dispose();
      timg = Requests.FixedSize(timg, 600, 600);
      ....
      timg.Dispose();

你有 2 timg 这里.. 一个是原始的,第二个是你从Requests.FixedSize 回来的。您传递给Requests.FixedSizetimg 确实不会被处理掉。

【讨论】:

  • 大约 100 个文件夹后,它似乎保存得很好。使用 Process Explorer,在其他控件上似乎仍然有其他东西泄漏,但它可能与您发现的相似。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-11
  • 2016-01-13
相关资源
最近更新 更多