DataAdapter.Fill到DataSet的内存泄漏

时间:2015-09-29 03:02:15

标签: c# multithreading unity3d memory-leaks dataset

我有一个简单的程序,它从数据库中获取数据并通过DataAdapter.Fill()将其存储在DataSet中。

我面临的问题是程序的内存大小不断增加。使用进程资源管理器,我监视程序的虚拟大小。这些过程在独立版本中运行(不在统一编辑器中)。

程序启动后2分钟,程序的虚拟大小为824,444K,但程序运行30分钟,程序的虚拟大小增加到1,722,340K。

(无法上传屏幕截图)https://drive.google.com/open?id=0B0DwzunTEqfKcDhHcXRmV2twUEE

程序只包含2个简单的脚本:一个辅助类SQLController,用于管理从数据库到数据集的加载,以及一个单行动脚本DataProducer,它定期轮询数据库,使用SQLController对象进行“更新”。

SQLController:

if ($result > 0) {
    while($row = $stmt->fetch()) {

        //SETUP HOURS FOR EACH DAY
        $timesSun = $row['sunday'];
        $timesMon = $row['monday'];
        $timesTue = $row['tuesday'];
        $timesWed = $row['wednesday'];
        $timesThu = $row['thursday'];
        $timesFri = $row['friday'];
        $timesSat = $row['saturday'];

        //SEPARATE DAY & NIGHT
        $sepDNsun = explode('/',$timesSun);
        $sepDNmon = explode('/',$timesMon);
        $sepDNtue = explode('/',$timesTue);
        $sepDNwed = explode('/',$timesWed);
        $sepDNthu = explode('/',$timesThu);
        $sepDNfri = explode('/',$timesFri);
        $sepDNsat = explode('/',$timesSat);

        //SEPARATE OPEN AND CLOSE
        $daySun = explode('to',$sepDNsun[0]);
        $nightSun = explode('to',$sepDNsun[1]);
        $dayMon = explode('to',$sepDNmon[0]);
        $nightMon = explode('to',$sepDNmon[1]);
        $dayTue = explode('to',$sepDNtue[0]);
        $nightTue = explode('to',$sepDNtue[1]);
        $dayWed = explode('to',$sepDNwed[0]);
        $nightWed = explode('to',$sepDNwed[1]);
        $dayThu = explode('to',$sepDNthu[0]);
        $nightThu = explode('to',$sepDNthu[1]);
        $dayFri = explode('to',$sepDNfri[0]);
        $nightFri = explode('to',$sepDNfri[1]);
        $daySat = explode('to',$sepDNsat[0]);
        $nightSat = explode('to',$sepDNsat[1]);

        //SET OPEN & CLOSE
        $dayOpenSun = $daySun[0];
        $dayCloseSun = $daySun[1];
        $nightOpenSun = $nightSun[0];
        $nightCloseSun = $nightSun[1];

        $dayOpenMon = $dayMon[0];
        $dayCloseMon = $dayMon[1];
        $nightOpenMon = $nightMon[0];
        $nightCloseMon = $nightMon[1];

        $dayOpenTue = $dayTue[0];
        $dayCloseTue = $dayTue[1];
        $nightOpenTue = $nightTue[0];
        $nightCloseTue = $nightTue[1];

        $dayOpenWed = $dayWed[0];
        $dayCloseWed = $dayWed[1];
        $nightOpenWed = $nightWed[0];
        $nightCloseWed = $nightWed[1];

        $dayOpenThu = $dayThu[0];
        $dayCloseThu = $dayThu[1];
        $nightOpenThu = $nightThu[0];
        $nightCloseThu = $nightThu[1];

        $dayOpenFri = $dayFri[0];
        $dayCloseFri = $dayFri[1];
        $nightOpenFri = $nightFri[0];
        $nightCloseFri = $nightFri[1];

        $dayOpenSat = $daySat[0];
        $dayCloseSat = $daySat[1];
        $nightOpenSat = $nightSat[0];
        $nightCloseSat = $nightSat[1];

        //SET STORE OPENING HOURS
        $storeSchedule = [
            'Sun' => [$dayOpenSun => $dayCloseSun, $nightOpenSun => $nightCloseSun],
            'Mon' => [$dayOpenMon => $dayCloseMon, $nightOpenMon => $nightCloseMon],
            'Tue' => [$dayOpenTue => $dayCloseTue, $nightOpenTue => $nightCloseTue],
            'Wed' => [$dayOpenWed => $dayCloseWed, $nightOpenWed => $nightCloseWed],
            'Thu' => [$dayOpenThu => $dayCloseThu, $nightOpenThu => $nightCloseThu],
            'Fri' => [$dayOpenFri => $dayCloseFri, $nightOpenFri => $nightCloseFri],
            'Sat' => [$dayOpenSat => $dayCloseSat, $nightOpenSat => $nightCloseSat]
        ];

        // current or user supplied UNIX timestamp
        $timestamp = time();

        // default status
        $status = $lang["NO-READY"];

        // get current time object
        $currentTime = (new DateTime())->setTimestamp($timestamp);

        // loop through time ranges for current day
        foreach ($storeSchedule[date('D', $timestamp)] as $startTime => $endTime) {

            // create time objects from start/end times
            $startTime = DateTime::createFromFormat('G:i', $startTime);
            $endTime   = DateTime::createFromFormat('G:i', $endTime);

            // check if current time is within a range
            if (($startTime < $currentTime) && ($currentTime < $endTime)) {
                $status = $lang["READY"];
                break;
            }
        }

        //OUTPUT CONTENT
        echo '<li>
                  <div class="rest-list-content">
                      <a href="'. $location .'/restaurants/'. $row["rest_url"] .'">
                      <img src="images/all_rest/'. $row["rest_logo"] .'" alt="'. $row["rest_name"] .'">
                      <h1>'. $row["rest_name"] .'</h1>
                      <p>Cuisine: <span>'. $row["cuisine_name"] .'</span></p>
                      <p>Minimun Order: <span>$'. $row["rest_min_order"] .'</span></p>
                      <p class="availability">'. $status .'</p>
                      </a>
                  </div>
              </li>';
    }
} else {
    echo "0 results";
}

DataProducer

 using UnityEngine;
 using System.Collections;
 using System.Data;
 using System.Data.Sql;
 using System.Data.SqlClient;
 using UnityEngine.UI;
 using System.Threading;

 public class SQLController {

     string connectionString;
     string dataSource, catalog, uid, pwd;

     DataSet dat_set;

     public SQLController(string dataSource, string catalog, string uid, string pwd)
     {
         this.dataSource = dataSource;
         this.catalog = catalog;
         this.uid = uid;
         this.pwd = pwd;
     }

     /// <summary>
     /// Open a connectio to the database and query it for data with the statement input
     /// </summary>
     /// <param name="statement"> The query statement to be used to query the server</param>
     /// <returns></returns>
     public DataSet Load(string statement, string name)
     {
         connectionString = string.Format("data source={0};initial catalog={1};uid={2};pwd={3}", dataSource, catalog, uid, pwd);

         using (SqlConnection dbcon = new SqlConnection(connectionString))
         using (SqlDataAdapter dataAdapter = new SqlDataAdapter(statement, dbcon))
         {
             dat_set = new System.Data.DataSet();

             dbcon.Open();
             dataAdapter.Fill(dat_set, name);  
         }

         return dat_set;
     }
 }

我不确定究竟是什么导致了内存泄漏,但是当我从线程

更改了加载时
 using UnityEngine;
 using System.Collections;
 using System.Threading;
 using System.Data;
 using UnityEngine.UI;
 using SimpleJSON;

 public class DataProducer : MonoBehaviour {


     public GameObject textObj;

     private string[] _configData;
     private string _outputText;
     private SQLController _sqlController;

     private bool _toggle = true;
     private bool _updating = false;

     private DataSet _dataSetCache;
     private Thread _dataGrabThread;

     // Use this for initialization
     void Start () {
         _configData = new string[5];

         if (LoadFromConfigFile())
         {
             StartCoroutine(LoadFromDB()); 
         }
     }

     // Update is called once per frame
     void Update () {
         textObj.GetComponent<Text>().text = _outputText;    
     }

     public void OnDisable()
     {
         // stop any running thread
         if (null != _dataGrabThread && _dataGrabThread.IsAlive)
         {
             _dataGrabThread.Abort();
         }
     }



     IEnumerator LoadFromDB()
     {
         while (true)
         {
             if (_updating)
             {
                 Debug.Log("Data System Poll DataBase ignored");
             }
             else
             {
                 _updating = true;

                 _dataGrabThread = new Thread(Load);

                 _dataGrabThread.IsBackground = true;
                 _dataGrabThread.Start();
             }

             yield return new WaitForSeconds(10f);
         }

     }

     void Load()
     {
         string statement;
         if (_toggle)
         {
             _toggle = !_toggle;
             statement = "SELECT TOP 100000 [AIIDX],[LASTATTACKDATE],[LASTRECEIVEDDATE],[DEVICEID],[INSTANCES],[ATTACKTYPE],[SEVERITY],[STATUS] FROM AI (NOLOCK)";
         }
         else
         {
             _toggle = !_toggle;
             statement = "SELECT TOP 100000 [AIIDX],[LASTATTACKDATE],[LASTRECEIVEDDATE],[DEVICEID],[SEVERITY],[STATUS] FROM AI (NOLOCK)";
         }

         _sqlController = new SQLController(_configData[0], _configData[1], _configData[2], _configData[3]);

         _outputText = "Loading";
         _dataSetCache = _sqlController.Load(statement, "TestObject");
         PrintDataSet();
         _updating = false;
     }

     /// <summary>
     /// Convert datatable into string and print it out through a text object
     /// </summary>
     void PrintDataSet()
     {
         if (null == _dataSetCache)
         {
             return;
         }

         DataTable dt = _dataSetCache.Tables["TestObject"];

         if (null == dt)
         {
             return;
         }


         System.Text.StringBuilder builder = new System.Text.StringBuilder();

         for (int i = 0; i < 20; ++i)
         {
             builder.AppendFormat("{0,-5}", (i + 1) + ".");
             //comp.text += string.Format("{0,-5}", (i + 1) + ".");
             DataRow dr = dt.Rows[i];
             for (int j = 0; j < dt.Columns.Count; ++j)
             {
                 builder.AppendFormat("{0, -30}", dr[dt.Columns[j]].ToString());
             }
             builder.Append("\n");
         }

         _outputText = builder.ToString();

         builder = null;

     }

     bool LoadFromConfigFile()
     {
         string line;
         using (System.IO.StreamReader file = new System.IO.StreamReader("./config.txt"))
         {
             if (file == null)
             {
                 return false;
             }

             int index = 0;
             while ((line = file.ReadLine()) != null)
             {
                 if (index > _configData.Length)
                 {
                     Debug.LogError("Invalid Config file");
                     return false;
                 }

                 _configData[index++] = line;
             }

             //if the config file does not consist of 5 data
             if (index < _configData.Length)
             {
                 Debug.LogError("Invalid Config file");
                 return false;
             }

             return true;
         }
     }    
 }

在主线程中运行,

 _dataGrabThread = new Thread(Load);

进程虚拟大小虽然仍然增加,但速度较慢。从828,312K,2分钟到1,083,908K,40分钟。

0 个答案:

没有答案