none
为什么会内存溢出呢? RRS feed

  • 问题

  • //我用的是Sql2008 中的FileStream特性取出大文件,约500M
    
    //我的代码如下:
    
       SqlConnection conn = new SqlConnection("server=my\\data2008;user id=zjh;password=321321;database=test");
       conn.Open(); 
       SqlCommand cmd = conn.CreateCommand(); 
       cmd.CommandText = "select FSData from FileStreamTable where fs_id='"+textBox1.Text.Trim()+"'";
       cmd.CommandTimeout = 999999;
       SqlDataReader dr = cmd.ExecuteReader(); 
        SaveFileDialog dialog = new SaveFileDialog(); 
        dialog.Filter = "音乐文件(*.*)|*.*";
        if (dialog.ShowDialog() == DialogResult.OK)
        {
         dr.Read();
         System.Data.SqlTypes.SqlBinary result = dr.GetSqlBinary(0);     
         FileStream fs = new FileStream(dialog.FileName, FileMode.Create);
         fs.Write(result.Value, 0, result.Length);//这里会内存溢出???
         dr.Close();
         fs.Flush();
         fs.Dispose();
        }
    
    
    2010年11月12日 3:44

答案

  • using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.Data.SqlClient;
    using WindowsApplication1.TestDataSetTableAdapters;
    
    namespace WindowsApplication1
    {
     public partial class Form1 : Form
     {
      public Form1()
      {
       InitializeComponent();
      }
    
      private void button1_Click(object sender, EventArgs e)
      {
       if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
       {
        try
        {
    
         Stream fs = this.openFileDialog1.OpenFile();
         FileStreamTableTableAdapter adpter = new FileStreamTableTableAdapter();
         byte[] fileData = new byte[fs.Length];
         fs.Read(fileData, 0, (int)fileData.Length);
         adpter.Insert(fileData);
         MessageBox.Show("Succeed!");
        }
        catch (Exception ex)
        {
         MessageBox.Show(ex.Message);
        }
       }
      }
    
      private void button2_Click(object sender, EventArgs e)
      {
       if (this.saveFileDialog1.ShowDialog() == DialogResult.OK)
       {
        FileStreamTableTableAdapter adpter = new FileStreamTableTableAdapter();
    
        byte[] fileData = (byte[])adpter.GetData().Rows[0][0];
        FileStream fs = new FileStream(this.saveFileDialog1.FileName, FileMode.Create);
        fs.Write(fileData, 0, fileData.Length);
        fs.Flush();
        fs.Dispose();
    
       }
      }
     }
    }
    

    你好 我做了上面的测试:

    最开始我尝试插入一个600MB的文件,结果本地的SQL server服务器和这个程序都占用了大量的内存 大概都是600MB以上

    而且一直插入不进去 因为数据库超时虽然我已经设置不超时

    后来我插入一个265MB大小的,第一次是出现和你一样的情况,内存溢出

    我restart了数据库服务,就成功上传。

    再次restar 执行和你一样的操作即查询数据写入到本地磁盘文件

    这个时候 数据库占用300多MB内存 这个程序占用500多MB内存,不过最终还是成功了

    但是我相信如果我这里也是查询出来600多MB 那么内存占用估计超过1GB 就很有可能出现内存溢出的情况了。

     

    所以对于你这种情况,非必须的情况下,我的建议是数据库中不要保存这么大的文件,而是保存文件路径,以ftp等方式上传文件至数据库。

    另外你还可以采用分批次分段读取 然后写入到磁盘中,http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldatareader.getbytes%28v=VS.80%29.aspx

     


    Raymond Tang (Microsoft C# MVP)
    Denn Ich Gehoer nur mir
    .NET交流群71840452
    微软中文论坛同城社区成都QQ群:74268428
    My Chinese Blog
    Chengdu,Sichuan Province,China
    2010年11月12日 8:22
    版主
  • Hi Raymond

     

    我看了您提供的论坛地址,有一个问题:FileStreamTableTableAdapter是个什么对象?我google没有结果,是您自己写的Class?能否share一下相关信息?


    IMHO,我个人认为楼主的问题可能与数据库没有关系,因为是OutofMemoryException!


    我个人认为是因为一次性读取大文件到Streeam里(也就是内存当中),导致CLR无法继续开辟内存空间以存储文件流,而抛出OutOfMemoryException

    Refer:http://msdn.microsoft.com/en-us/library/system.outofmemoryexception.aspx


    而解决方案基本上至少有2种:

    1。 分块读取/写入文件到流(内存),分块写入,下面是分块读/写的示例代码

    while (reader.Peek() >= 0) 
    {
    	char[] c = new char[50 * 1024 * 1024];
    	reader.Read(c, 0, c.Length);
    	
    	using (StreamWriter writer = File.Create("C:\\somefile.dat"))
    	{
    		writer.Write(c, 0, c.Length);
    		write.Flush(true);
    		
    		writer.Close();
    	}
    } 
    

     

    2。加大内存(想想一下如果服务器有16G内存,则正常情况下读取500M的文件不会抛出OutOfMemoryException)。


    Please feel free to let me know if you have any further issues, thanks!


    Happy Coding:)
    Wayne Ye - Senior Software Development Engineer
    Personal Website: http://WayneYe.com





    • 已标记为答案 zjh111 2010年12月3日 17:17
    2010年11月12日 9:07
  • dear
    使用分批读/写挡的方法试试,主要是用二进位处理方式,读入缓冲区。
    可用
    BufferedStream + FileStream
    BufferedStream + MemoryStream
    以下几个例子,看看能解决你的问题
    http://www.dotblogs.com.tw/yc421206/archive/2009/11/01/11370.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/27/11312.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/28/11324.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/30/11357.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/29/11349.aspx
    秘訣無它,唯勤而已
    2010年11月12日 10:53
  • Hi Raymond

     

    我看了您提供的论坛地址,有一个问题:FileStreamTableTableAdapter 是个什么对象?我google没有结果,是您自己写的Class?能否share一下相关信息?


    IMHO,我个人认为楼主的问题可能与数据库没有关系 ,因为是OutofMemoryException !


    我个人认为是因为一次性读取大文件到Streeam里(也就是内存当中),导致CLR无法继续开辟内存空间以存储文件流,而抛出OutOfMemoryException

    Refer:http://msdn.microsoft.com/en-us/library/system.outofmemoryexception.aspx


    而解决方案基本上至少有2种:

    1。 分块读取/写入文件到流(内存),分块写入,下面是分块读/写的示例代码

    while
    
    
     (reader.Peek() >= 0) 
    {
    	char
    
    
    [] c = new
    
    
     char
    
    
    [50 * 1024 * 1024];
    	reader.Read(c, 0, c.Length);
    	
    	using
    
    
     (StreamWriter writer = File.Create("C:\\somefile.dat"
    
    
    ))
    	{
    		writer.Write(c, 0, c.Length);
    		write.Flush(true
    
    
    );
    		
    		writer.Close();
    	}
    } 
    

     

    2。加大内存(想想一下如果服务器有16G内存,则正常情况下读取500M的文件不会抛出OutOfMemoryException)。


    Please feel free to let me know if you have any further issues, thanks!


    Happy Coding:)
    Wayne Ye - Senior Software Development Engineer
    Personal Website:   http://WayneYe.com






    Hi Wayne,

    FileStreamTableTableAdapter是我建立的强类型的DataSet VS自动生成的一个类,它包含了Adapter、Connection等,这样可以方便的直接使用相关的方法

    IMHO,我个人认为楼主的问题可能与数据库没有关系 ,因为是OutofMemoryException !

    你这里的说法是正确的,这个问题跟数据库没多大关系,我只是在结尾建议楼主不要将这么大的文件存入数据库,存入磁盘然后保存路径可能会更好。

    分批次分段的读取应该是可以解决这个问题的,使用SqlDataReader读取 http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldatareader.getbytes%28v=VS.80%29.aspx

     


    Raymond Tang (Microsoft C# MVP)
    Denn Ich Gehoer nur mir
    .NET交流群71840452
    微软中文论坛同城社区成都QQ群:74268428
    My Chinese Blog
    Chengdu,Sichuan Province,China
    2010年11月12日 11:33
    版主

全部回复

  • //我用的是Sql2008 中的FileStream特性取出大文件,约500M
    
    //我的代码如下:
    
       SqlConnection conn = new SqlConnection("server=my\\data2008;user id=zjh;password=321321;database=test");
       conn.Open(); 
       SqlCommand cmd = conn.CreateCommand(); 
       cmd.CommandText = "select FSData from FileStreamTable where fs_id='"+textBox1.Text.Trim()+"'";
       cmd.CommandTimeout = 999999;
       SqlDataReader dr = cmd.ExecuteReader(); 
        SaveFileDialog dialog = new SaveFileDialog(); 
        dialog.Filter = "音乐文件(*.*)|*.*";
        if (dialog.ShowDialog() == DialogResult.OK)
        {
         dr.Read();
         System.Data.SqlTypes.SqlBinary result = dr.GetSqlBinary(0);     
         FileStream fs = new FileStream(dialog.FileName, FileMode.Create);
         fs.Write(result.Value, 0, result.Length);//这里会内存溢出???
         dr.Close();
         fs.Flush();
         fs.Dispose();
        }
    
    
    2010年11月12日 3:42
  • 要看你数据库的结构怎么定义的

    SqlBinary较少使用

    一般是直接获取byte[]

    2010年11月12日 8:05
  • using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using System.Data.SqlClient;
    using WindowsApplication1.TestDataSetTableAdapters;
    
    namespace WindowsApplication1
    {
     public partial class Form1 : Form
     {
      public Form1()
      {
       InitializeComponent();
      }
    
      private void button1_Click(object sender, EventArgs e)
      {
       if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
       {
        try
        {
    
         Stream fs = this.openFileDialog1.OpenFile();
         FileStreamTableTableAdapter adpter = new FileStreamTableTableAdapter();
         byte[] fileData = new byte[fs.Length];
         fs.Read(fileData, 0, (int)fileData.Length);
         adpter.Insert(fileData);
         MessageBox.Show("Succeed!");
        }
        catch (Exception ex)
        {
         MessageBox.Show(ex.Message);
        }
       }
      }
    
      private void button2_Click(object sender, EventArgs e)
      {
       if (this.saveFileDialog1.ShowDialog() == DialogResult.OK)
       {
        FileStreamTableTableAdapter adpter = new FileStreamTableTableAdapter();
    
        byte[] fileData = (byte[])adpter.GetData().Rows[0][0];
        FileStream fs = new FileStream(this.saveFileDialog1.FileName, FileMode.Create);
        fs.Write(fileData, 0, fileData.Length);
        fs.Flush();
        fs.Dispose();
    
       }
      }
     }
    }
    

    你好 我做了上面的测试:

    最开始我尝试插入一个600MB的文件,结果本地的SQL server服务器和这个程序都占用了大量的内存 大概都是600MB以上

    而且一直插入不进去 因为数据库超时虽然我已经设置不超时

    后来我插入一个265MB大小的,第一次是出现和你一样的情况,内存溢出

    我restart了数据库服务,就成功上传。

    再次restar 执行和你一样的操作即查询数据写入到本地磁盘文件

    这个时候 数据库占用300多MB内存 这个程序占用500多MB内存,不过最终还是成功了

    但是我相信如果我这里也是查询出来600多MB 那么内存占用估计超过1GB 就很有可能出现内存溢出的情况了。

     

    所以对于你这种情况,非必须的情况下,我的建议是数据库中不要保存这么大的文件,而是保存文件路径,以ftp等方式上传文件至数据库。

    另外你还可以采用分批次分段读取 然后写入到磁盘中,http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldatareader.getbytes%28v=VS.80%29.aspx

     


    Raymond Tang (Microsoft C# MVP)
    Denn Ich Gehoer nur mir
    .NET交流群71840452
    微软中文论坛同城社区成都QQ群:74268428
    My Chinese Blog
    Chengdu,Sichuan Province,China
    2010年11月12日 8:22
    版主
  • 你好 请参考我给你的回复

    http://social.msdn.microsoft.com/Forums/zh-CN/2212/thread/40a09603-57f2-407c-a49d-a0cdbd4ee79f

     


    Raymond Tang (Microsoft C# MVP)
    Denn Ich Gehoer nur mir
    .NET交流群71840452
    微软中文论坛同城社区成都QQ群:74268428
    My Chinese Blog
    Chengdu,Sichuan Province,China
    2010年11月12日 8:31
    版主
  • Hi Raymond

     

    我看了您提供的论坛地址,有一个问题:FileStreamTableTableAdapter是个什么对象?我google没有结果,是您自己写的Class?能否share一下相关信息?


    IMHO,我个人认为楼主的问题可能与数据库没有关系,因为是OutofMemoryException!


    我个人认为是因为一次性读取大文件到Streeam里(也就是内存当中),导致CLR无法继续开辟内存空间以存储文件流,而抛出OutOfMemoryException

    Refer:http://msdn.microsoft.com/en-us/library/system.outofmemoryexception.aspx


    而解决方案基本上至少有2种:

    1。 分块读取/写入文件到流(内存),分块写入,下面是分块读/写的示例代码

    while (reader.Peek() >= 0) 
    {
    	char[] c = new char[50 * 1024 * 1024];
    	reader.Read(c, 0, c.Length);
    	
    	using (StreamWriter writer = File.Create("C:\\somefile.dat"))
    	{
    		writer.Write(c, 0, c.Length);
    		write.Flush(true);
    		
    		writer.Close();
    	}
    } 
    

     

    2。加大内存(想想一下如果服务器有16G内存,则正常情况下读取500M的文件不会抛出OutOfMemoryException)。


    Please feel free to let me know if you have any further issues, thanks!


    Happy Coding:)
    Wayne Ye - Senior Software Development Engineer
    Personal Website: http://WayneYe.com





    • 已标记为答案 zjh111 2010年12月3日 17:17
    2010年11月12日 9:07
  • dear
    使用分批读/写挡的方法试试,主要是用二进位处理方式,读入缓冲区。
    可用
    BufferedStream + FileStream
    BufferedStream + MemoryStream
    以下几个例子,看看能解决你的问题
    http://www.dotblogs.com.tw/yc421206/archive/2009/11/01/11370.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/27/11312.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/28/11324.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/30/11357.aspx
    http://www.dotblogs.com.tw/yc421206/archive/2009/10/29/11349.aspx
    秘訣無它,唯勤而已
    2010年11月12日 10:53
  • Hi Raymond

     

    我看了您提供的论坛地址,有一个问题:FileStreamTableTableAdapter 是个什么对象?我google没有结果,是您自己写的Class?能否share一下相关信息?


    IMHO,我个人认为楼主的问题可能与数据库没有关系 ,因为是OutofMemoryException !


    我个人认为是因为一次性读取大文件到Streeam里(也就是内存当中),导致CLR无法继续开辟内存空间以存储文件流,而抛出OutOfMemoryException

    Refer:http://msdn.microsoft.com/en-us/library/system.outofmemoryexception.aspx


    而解决方案基本上至少有2种:

    1。 分块读取/写入文件到流(内存),分块写入,下面是分块读/写的示例代码

    while
    
    
     (reader.Peek() >= 0) 
    {
    	char
    
    
    [] c = new
    
    
     char
    
    
    [50 * 1024 * 1024];
    	reader.Read(c, 0, c.Length);
    	
    	using
    
    
     (StreamWriter writer = File.Create("C:\\somefile.dat"
    
    
    ))
    	{
    		writer.Write(c, 0, c.Length);
    		write.Flush(true
    
    
    );
    		
    		writer.Close();
    	}
    } 
    

     

    2。加大内存(想想一下如果服务器有16G内存,则正常情况下读取500M的文件不会抛出OutOfMemoryException)。


    Please feel free to let me know if you have any further issues, thanks!


    Happy Coding:)
    Wayne Ye - Senior Software Development Engineer
    Personal Website:   http://WayneYe.com






    Hi Wayne,

    FileStreamTableTableAdapter是我建立的强类型的DataSet VS自动生成的一个类,它包含了Adapter、Connection等,这样可以方便的直接使用相关的方法

    IMHO,我个人认为楼主的问题可能与数据库没有关系 ,因为是OutofMemoryException !

    你这里的说法是正确的,这个问题跟数据库没多大关系,我只是在结尾建议楼主不要将这么大的文件存入数据库,存入磁盘然后保存路径可能会更好。

    分批次分段的读取应该是可以解决这个问题的,使用SqlDataReader读取 http://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqldatareader.getbytes%28v=VS.80%29.aspx

     


    Raymond Tang (Microsoft C# MVP)
    Denn Ich Gehoer nur mir
    .NET交流群71840452
    微软中文论坛同城社区成都QQ群:74268428
    My Chinese Blog
    Chengdu,Sichuan Province,China
    2010年11月12日 11:33
    版主
  • Hi Raymond

     

    OK,没有问题:)



    Happy Coding:)
    Wayne Ye - Senior Software Development Engineer
    Personal Website:   http://WayneYe.com


    2010年11月15日 3:08