Queue.Dequeue 及时释放资源

takako_mu 2012-03-15 04:58:59
我有一个全局变量Queue<T>,里面塞N笔对象,然后一笔笔移出对象,为什么该对象对应的内存资源不释放。

有什么办法可以做到及时释放呢,即移一笔,对应的内存就释放掉。

因设计问题,只能是全局变量,这Queue永远出不了作用域,是不是就只能永远这么涨下去。


PS:T是一个蛮复杂的结构体,所以内存涨的很快。
...全文
1164 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
viki117 2012-03-16
  • 打赏
  • 举报
回复
内存释放的问题在wpf里面更严重
viki117 2012-03-16
  • 打赏
  • 举报
回复
不会及时的,看CG的的原理吧~~就算null了,或者dispose了,也不保证立刻释放,还是要看CG的,手动掉用GC.Collect ()倒是可以,不过只是不建议,说是会影响性能.
qq4004229 2012-03-16
  • 打赏
  • 举报
回复
GC.Collect
用这个就可以了.但是你要考虑会不会对其他变量有影响
takako_mu 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 foreachif 的回复:]
引用 13 楼 takako_mu 的回复:
引用 8 楼 foreachif 的回复:
会不会是Queue的容量问题。可尝试使用TrimExcess方法以减少容量

没用。

怎么说也该有点用吧。
因为要兼顾性能,避免频繁的内存重新申请,只有在使用多次Dequeue后调用可能才有实际效果
很难达到“移一笔,对应的内存就释放掉”(假设没有内存泄漏),除非不考虑性能,否则使用Queu……
[/Quote]
我测的时候两种方式都用过,其中一种就是你说的在Dequeue最后一笔时再TrimExcess,发现没有效果。
takako_mu 2012-03-16
  • 打赏
  • 举报
回复
谢谢各位的帮助,我现在将测试代码发出来,各位可以测下虚拟内存,Dequeue之后虚拟内存还是那么大。

希望有更好的方法提出来,感谢!


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.Serialization;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Runtime.Serialization.Json;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private SafeQueue<JobDetail> TestQueue = new SafeQueue<JobDetail>();

public Form1()
{
InitializeComponent();
}

private void btnEnqueue_Click(object sender, EventArgs e)
{
for (int i = 0; i < int.Parse(txtEnQueue.Text.Trim()); i++)
{
SendJobInfo();
}
MessageBox.Show("Done!");
}

private void btnDequeue_Click(object sender, EventArgs e)
{
for (int i = 0; i < int.Parse(txtDequeue.Text.Trim()); i++)
{
var j = TestQueue.Dequeue();
}
MessageBox.Show("Done!");
}

public void SendJobInfo()
{
string sInfo = "{\"JobID\":\"" + System.Guid.NewGuid().ToString() + "\",";
sInfo += "\"JobNum\":2,";
sInfo += "\"MsgType\":\"Job_info\",";
sInfo += "\"UserName\":\"takako_mu\",";
sInfo += "\"DocName\":\"Test.txt\",";
sInfo += "\"SubmitTime\":\"" + DateTime.Now.ToString() + "\"}";

var jobDetail = Utility.DeSerializerFromJson<JobDetail>(sInfo);
TestQueue.TryEnqueue(jobDetail, 10);
}
}

public class SafeQueue<T>
{
// A queue that is protected by Monitor.
private Queue<T> m_inputQueue = new Queue<T>();

// Lock the queue and add an element.
public void Enqueue(T qValue)
{
// Request the lock, and block until it is obtained.
Monitor.Enter(m_inputQueue);
try
{
// When the lock is obtained, add an element.
m_inputQueue.Enqueue(qValue);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
}

// Try to add an element to the queue: Add the element to the queue
// only if the lock is immediately available.
public bool TryEnqueue(T qValue)
{
// Request the lock.
if (Monitor.TryEnter(m_inputQueue))
{
try
{
m_inputQueue.Enqueue(qValue);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return true;
}
else
{
return false;
}
}

// Try to add an element to the queue: Add the element to the queue
// only if the lock becomes available during the specified time
// interval.
public bool TryEnqueue(T qValue, int waitTime)
{
// Request the lock.
if (Monitor.TryEnter(m_inputQueue, waitTime))
{
try
{
m_inputQueue.Enqueue(qValue);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}
return true;
}
else
{
return false;
}
}

// Lock the queue and dequeue an element.
public T Dequeue()
{
T retval;

// Request the lock, and block until it is obtained.
Monitor.Enter(m_inputQueue);
try
{
// When the lock is obtained, dequeue an element.
if (m_inputQueue.Count > 0)
{
retval = m_inputQueue.Dequeue();
m_inputQueue.TrimExcess();
}
else
retval = default(T);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(m_inputQueue);
}

return retval;
}

public void Clear()
{
m_inputQueue.Clear();
}
}

[DataContract]
public class JobDetail
{
/// <summary>
/// Job ID (GUID) QueueID
/// </summary>
[DataMember(Name = "JobID")]
public string JobID { get; set; }

/// <summary>
/// Message Type (Not Used)
/// </summary>
[DataMember(Name = "MsgType")]
public string MsgType { get; set; }

/// <summary>
/// Document Name
/// </summary>
[DataMember(Name = "DocName")]
public string DocName { get; set; }

/// <summary>
/// User name
/// </summary>
[DataMember(Name = "UserName", IsRequired = true)]
public string UserName { get; set; }

[DataMember(Name = "SubmitTime")]
public string SubmitTime { get; set; }
}

public class Utility
{
public static string GetSerializerXml<T>(T t)
{
var xmlSerializer = new XmlSerializer(t.GetType());
var memStream = new MemoryStream();
var xmlTextWriter = new XmlTextWriter(memStream, Encoding.UTF8);
xmlSerializer.Serialize(xmlTextWriter, t);
memStream = xmlTextWriter.BaseStream as MemoryStream;
if (memStream != null)
{
var utf8Encoding = new UTF8Encoding();
string strXml = utf8Encoding.GetString(memStream.ToArray());
return strXml;
}

xmlTextWriter.Close();

return string.Empty;
}

public static string GetSerializerJson<T>(T t)
{
var jsonSerializer = new DataContractJsonSerializer(typeof(T));

using (MemoryStream memStream = new MemoryStream())
{
jsonSerializer.WriteObject(memStream, t);

if (memStream != null)
{
var utf8Encoding = new UTF8Encoding();
string strJson = utf8Encoding.GetString(memStream.ToArray());
return strJson;
}
}
return string.Empty;
}

public static T DeSerializerFromXml<T>(string xml)
{
var xmlSerializer = new XmlSerializer(typeof(T));
var utf8Encoding = new UTF8Encoding();

MemoryStream memStream = new MemoryStream(utf8Encoding.GetBytes(xml));

var t = (T)xmlSerializer.Deserialize(memStream);
memStream.Close();

return t;
}

public static T DeSerializerFromJson<T>(string json)
{
var jsonSerializer = new DataContractJsonSerializer(typeof(T));
var utf8Encoding = new UTF8Encoding();

MemoryStream memStream = new MemoryStream(utf8Encoding.GetBytes(json));

var t = (T)jsonSerializer.ReadObject(memStream);

memStream.Close();

return t;
}
}
}

WAN 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 takako_mu 的回复:]
引用 8 楼 foreachif 的回复:
会不会是Queue的容量问题。可尝试使用TrimExcess方法以减少容量

没用。
[/Quote]
怎么说也该有点用吧。
因为要兼顾性能,避免频繁的内存重新申请,只有在使用多次Dequeue后调用可能才有实际效果
很难达到“移一笔,对应的内存就释放掉”(假设没有内存泄漏),除非不考虑性能,否则使用Queue总是会有额外的内存损耗
  • 打赏
  • 举报
回复
GC.WaitForFullGCComplete 方法 查询CLR引发的完整垃圾回收完成的状态。(默认只是回收托管资源)
  • 打赏
  • 举报
回复
多半有内存泄漏了,或者是哪个地方还有意想之外的引用,可以找个工具查一下
http://msdn.microsoft.com/zh-cn/magazine/cc163491.aspx

引一段MSDN:
托管线程的生存期并不依赖于创建它的 Thread 对象。如果您只是因为丢失了所有与 Thread 对象相关联的引用而不希望垃圾收集器将一个仍在运行的进程终止,这种不依赖性是非常有好处的。由此可见,垃圾收集器只是收集 Thread 对象,而非实际托管的线程。只有在其 ThreadProc 返回后或者自身被直接终止的情况下,托管线程才会退出(其线程堆栈的内存不会释放)。因此,如果托管线程的终止方式不正确,分配至其线程堆栈的内存就会发生泄漏。
takako_mu 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 foreachif 的回复:]
会不会是Queue的容量问题。可尝试使用TrimExcess方法以减少容量
[/Quote]
没用。
淘淘大师 2012-03-16
  • 打赏
  • 举报
回复
那只能先进行定期GC.Collect回收了
takako_mu 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 qldsrx 的回复:]
如果说,你的T设计的非常好,大小可以固定,那么你移除的对象即使不回收内存,下次添加新对象进入时,也不会重新分配空间,而是直接使用上次移除的对象占有的空间。只有当下次要获取连续空间时,找不到一个可用的连续空间,才会重新向系统申请新的内存空间,导致内存上涨。
如果每次都申请新的内存空间,并导致内存上涨,那么你是否调用GC.Collect 强制回收都一样,GC.Collect回收后,GC里面将清理一遍……
[/Quote]
我有用int来测,发现结果一样的,所以跟T的设计无关。
qldsrx 2012-03-16
  • 打赏
  • 举报
回复
如果说,你的T设计的非常好,大小可以固定,那么你移除的对象即使不回收内存,下次添加新对象进入时,也不会重新分配空间,而是直接使用上次移除的对象占有的空间。只有当下次要获取连续空间时,找不到一个可用的连续空间,才会重新向系统申请新的内存空间,导致内存上涨。
如果每次都申请新的内存空间,并导致内存上涨,那么你是否调用GC.Collect 强制回收都一样,GC.Collect回收后,GC里面将清理一遍,必然导致下次申请空间需要像系统申请,但是你既然总是要像系统申请内存(这肯定是设计问题),那么还不如调用GC.Collect把不能使用的内存缓存给清空了更好。
WAN 2012-03-16
  • 打赏
  • 举报
回复
会不会是Queue的容量问题。可尝试使用TrimExcess方法以减少容量
铜臂阿铁木 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 qldsrx 的回复:]

补充一个知识点:由于GC对0代垃圾的回收效率非常高(微软官方表示的),因此我们可以利用这点,在很大垃圾产生后,及时调用“GC.Collect(0);”,这样就会将刚刚产生的垃圾立刻清除,且对性能不造成任何影响。
不过有一点需要注意的,当GC每次Collect(包括自动的Collect操作)之后,所有未被清理的对象,其“代”很可能会自动+1,下次清理0代垃圾时就可能清理不到,因此不要随便调用“G……
[/Quote]

学习了
qldsrx 2012-03-16
  • 打赏
  • 举报
回复
补充一个知识点:由于GC对0代垃圾的回收效率非常高(微软官方表示的),因此我们可以利用这点,在很大垃圾产生后,及时调用“GC.Collect(0);”,这样就会将刚刚产生的垃圾立刻清除,且对性能不造成任何影响。
不过有一点需要注意的,当GC每次Collect(包括自动的Collect操作)之后,所有未被清理的对象,其“代”很可能会自动+1,下次清理0代垃圾时就可能清理不到,因此不要随便调用“GC.Collect(0);”,免得提高了一些垃圾的“代”,影响了快速清理。
铜臂阿铁木 2012-03-16
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 takako_mu 的回复:]

感谢大家的帮忙。我在刚刚又测了一次,结果比较满意。我之前担心垃圾回收机制不会回收这些资源,可事实上是会的。
我是这样测的:1次Equeue 10W笔,然后Dequeue全部,然后再Enque 10W,再Dequeue,重复操作第4次的时候虚拟内存从83M降到57M,看来垃圾回收机制还是会回收这段资源的。
[/Quote]

是的,更垃圾回收的层次是有关系的。
刚刚我试了试200w条,分20次Dequeue 每次100,000,不会回收,但是这时候往里面再添加,内存突然下来了,说明分代数了。

另外既然这地方性能这么敏感,把字符串拼接改成string.Format()吧,不然很多碎片。
takako_mu 2012-03-16
  • 打赏
  • 举报
回复
感谢大家的帮忙。我在刚刚又测了一次,结果比较满意。我之前担心垃圾回收机制不会回收这些资源,可事实上是会的。
我是这样测的:1次Equeue 10W笔,然后Dequeue全部,然后再Enque 10W,再Dequeue,重复操作第4次的时候虚拟内存从83M降到57M,看来垃圾回收机制还是会回收这段资源的。
takako_mu 2012-03-16
  • 打赏
  • 举报
回复
同样的代码我在C++中也试过,全局变量不释放,跟垃圾回收机制没关系吧???
nonocast 2012-03-15
  • 打赏
  • 举报
回复
回收与否和Enq/Deq无关
移出以后手动回收就是了,重写一下对象的回收机制
另外如果无法改变对象,那只能定期轮询GC.Collect,就像你说的下下策
takako_mu 2012-03-15
  • 打赏
  • 举报
回复
不过收你的启发,我觉得可以在程序里加个schedule,每小时去GC.Collect下。当然这是下下策。

如果有好的方法,欢迎提出。
加载更多回复(4)

111,126

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧