System.Windows.Forms.Timer 和 C# 垃圾收集
System.Windows.Forms.Timer and Garbage Collection with C#
如果我创建一个与 Form.Designer.cs 文件无关的 System.Windows.Forms.Timer(例如,我只是在我的代码中的任何地方实例化计时器),并且我创建了一个 System.ComponentModel.Container 组件并创建一个要添加到它的控件,比如一个 NotifyIcon(这个 notifyIcon 不会有与之关联的图标)并将该 notifyIcon 添加到我的组件对象然后实例化 forms.timer 并将该组件作为计时器构造函数的参数,如果计时器的启用 属性 在程序的生命周期内设置为 false 并且计时器在程序的生命周期内从未被丢弃,计时器是否会被 GC 编辑?或者只要不直接处理该组件,定时器就不会受到垃圾回收的影响吗?
这是一个代码示例:
NotifyIcon notifyIcon = new NotifyIcon();
notifyIcon.Visible = false;
System.ComponentModel.IContainer components = new System.ComponentModel.Container();
components.add(notifyIcon);
System.Windows.Forms.Timer formTimer = new System.Windows.Forms.Timer(components);
formTimer.Enabled = true;
formTimer.Enabled = false;
感谢阅读
理论
如果对象 A 持有对对象 B 的引用,并且您将 null 分配给 A,那么一段时间后 A 将被垃圾回收。如果还有其他人引用 B,则 B 不会被垃圾回收。但是,如果 A 是最后一个引用 B 的,那么 B 也会被收集:
Person Joseph = new Person
{
Id = 1,
Address = new Address {Id = 10, ...},
...
};
Joseph.Address = new Address {Id = 11, ...}
一段时间后,ID 为 10 的地址将被垃圾回收,因为没有人再持有对它的引用。
Person Mary = new Person
{
Id = 2,
Address = Joseph.Address,
...
};
Joseph = null;
一段时间后,Id 为 1 的 Person 将被垃圾回收,因为没有人再持有对它的引用。但是,Id 为 11 的地址不会被垃圾回收,因为 ID 为 2 的人持有对它的引用。
mary = null;
现在没有人持有对 ID 为 11 的地址的引用,因此它将被垃圾收集。
回到你的问题
- 您创建了一个新的 Container 对象
- 您创建了一个新的 NotifyIcon 对象。
- 您将此 NotifyIcon 对象添加到组件对象
- 您创建一个新的 Timer 对象,使用命令计时器将其自身添加到 Container 的构造函数。
所以 Container 对象引用了创建的 NotifyIcon 和 Timer 对象。只要这些对象没有从 Container 中移除,并且容器没有被 Disposed 也没有被垃圾回收,这个 Container 就会持有这些引用,因此 notifyIcon 和 Timer 都不会被垃圾回收。
components.Dispose();
根据 reference source of System.ComponentModels.Container 这将执行以下操作:
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (syncObj) {
while (siteCount > 0)
{
ISite site = sites[--siteCount];
site.Component.Site = null;
site.Component.Dispose();
}
sites = null;
components = null;
}
}
所以Container.Dispose()
会调用Dispose()
所有添加的组件,然后释放对添加组件的引用
- 如果您在其他地方引用了这些组件,那么这些组件将不会被垃圾回收。但是,由于它们是 Disposed,通常它们是不可用的。
- 如果 Container 是唯一具有引用的容器,则在
Container.Dispose()
之后,NotifyIcon 和 Timer 有资格被收集。
- 因为您仍然持有对 Container 的引用,所以不会收集 Container 本身。既然是Disposed,Container就不能再用了。
- 为确保容器被收集,要么让引用超出范围,要么为它分配 null。
在每个 Form.Dispose()
中,您会发现类似于以下的代码:
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
当表单被处置时,this.components 也被处置,这意味着它持有的所有 IComponents 都被处置,并且引用被删除。甚至对 Container.ComponentCollection 的引用也被删除了。请注意,该表单仍然包含对 this.components 的引用,因此即使不能再使用 this.components。
后者有点奇怪:如果您不再保留对 NotifyIcon / Timer 的引用,它们将被垃圾收集,但是 this.Components 不会,只要表单存在。如果 Form.Dispose 也发布了对 this.components
的引用,那就更好了
如果我创建一个与 Form.Designer.cs 文件无关的 System.Windows.Forms.Timer(例如,我只是在我的代码中的任何地方实例化计时器),并且我创建了一个 System.ComponentModel.Container 组件并创建一个要添加到它的控件,比如一个 NotifyIcon(这个 notifyIcon 不会有与之关联的图标)并将该 notifyIcon 添加到我的组件对象然后实例化 forms.timer 并将该组件作为计时器构造函数的参数,如果计时器的启用 属性 在程序的生命周期内设置为 false 并且计时器在程序的生命周期内从未被丢弃,计时器是否会被 GC 编辑?或者只要不直接处理该组件,定时器就不会受到垃圾回收的影响吗?
这是一个代码示例:
NotifyIcon notifyIcon = new NotifyIcon();
notifyIcon.Visible = false;
System.ComponentModel.IContainer components = new System.ComponentModel.Container();
components.add(notifyIcon);
System.Windows.Forms.Timer formTimer = new System.Windows.Forms.Timer(components);
formTimer.Enabled = true;
formTimer.Enabled = false;
感谢阅读
理论
如果对象 A 持有对对象 B 的引用,并且您将 null 分配给 A,那么一段时间后 A 将被垃圾回收。如果还有其他人引用 B,则 B 不会被垃圾回收。但是,如果 A 是最后一个引用 B 的,那么 B 也会被收集:
Person Joseph = new Person
{
Id = 1,
Address = new Address {Id = 10, ...},
...
};
Joseph.Address = new Address {Id = 11, ...}
一段时间后,ID 为 10 的地址将被垃圾回收,因为没有人再持有对它的引用。
Person Mary = new Person
{
Id = 2,
Address = Joseph.Address,
...
};
Joseph = null;
一段时间后,Id 为 1 的 Person 将被垃圾回收,因为没有人再持有对它的引用。但是,Id 为 11 的地址不会被垃圾回收,因为 ID 为 2 的人持有对它的引用。
mary = null;
现在没有人持有对 ID 为 11 的地址的引用,因此它将被垃圾收集。
回到你的问题
- 您创建了一个新的 Container 对象
- 您创建了一个新的 NotifyIcon 对象。
- 您将此 NotifyIcon 对象添加到组件对象
- 您创建一个新的 Timer 对象,使用命令计时器将其自身添加到 Container 的构造函数。
所以 Container 对象引用了创建的 NotifyIcon 和 Timer 对象。只要这些对象没有从 Container 中移除,并且容器没有被 Disposed 也没有被垃圾回收,这个 Container 就会持有这些引用,因此 notifyIcon 和 Timer 都不会被垃圾回收。
components.Dispose();
根据 reference source of System.ComponentModels.Container 这将执行以下操作:
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (syncObj) {
while (siteCount > 0)
{
ISite site = sites[--siteCount];
site.Component.Site = null;
site.Component.Dispose();
}
sites = null;
components = null;
}
}
所以Container.Dispose()
会调用Dispose()
所有添加的组件,然后释放对添加组件的引用
- 如果您在其他地方引用了这些组件,那么这些组件将不会被垃圾回收。但是,由于它们是 Disposed,通常它们是不可用的。
- 如果 Container 是唯一具有引用的容器,则在
Container.Dispose()
之后,NotifyIcon 和 Timer 有资格被收集。 - 因为您仍然持有对 Container 的引用,所以不会收集 Container 本身。既然是Disposed,Container就不能再用了。
- 为确保容器被收集,要么让引用超出范围,要么为它分配 null。
在每个 Form.Dispose()
中,您会发现类似于以下的代码:
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
当表单被处置时,this.components 也被处置,这意味着它持有的所有 IComponents 都被处置,并且引用被删除。甚至对 Container.ComponentCollection 的引用也被删除了。请注意,该表单仍然包含对 this.components 的引用,因此即使不能再使用 this.components。
后者有点奇怪:如果您不再保留对 NotifyIcon / Timer 的引用,它们将被垃圾收集,但是 this.Components 不会,只要表单存在。如果 Form.Dispose 也发布了对 this.components
的引用,那就更好了