这个情况,如何回收、销毁对象?

货郎大叔 2019-05-26 09:58:05

class Person
{
string Name;
public Timer timer;
public Person(string s)
{
Name = s;
timer = new Timer();
timer.Interval = 1000;
timer.Elapsed += timer_Elapsed;
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine(Name);
}
}
class Program
{
static void Main(string[] args)
{
Person b = new Person("张三");
b = new Person("李四");
Console.ReadKey();
}
}

上面的代码中,Person类中定义了一个定时器,当创建一个Person对象的时候,这个定时器就会启动,不断输出Name属性值。在Main函数中,给变量b赋值了一个Person对象,紧接着又重新new了一个Person对象。这样的结果是:不断输出“张三”、"李四"。那说明第一个Person对象没有被GC回收。

请问,如何销毁、回收第一个对象呢,在应用程序中,可能会遇到很多这种情况,稍不小心,就会存在对不用的对象有引用。如何回收呢?
...全文
2875 46 打赏 收藏 转发到动态 举报
写回复
用AI写文章
46 条回复
切换为时间正序
请发表友善的回复…
发表回复
ZhuCheng Xie 2019-06-05
  • 打赏
  • 举报
回复
timer一直在输出,怎么会是“对不用的对象有引用”?我无法时刻确定对象的“根”在堆上是否有引用,只有GC的回收算法知道
FF.. 2019-05-31
  • 打赏
  • 举报
回复
引用 48 楼 Shadowrabbit 的回复:
楼主真是写bug的天才,成功的抵制了回收gc
绝了
货郎大叔 2019-05-30
  • 打赏
  • 举报
回复
如果不是timer,如何知道一个对象被回收没有呢?是不是创建一个析构函数,然后在析构函数设置断点
jx315425246 2019-05-29
  • 打赏
  • 举报
回复

class Person
{
string Name;
public Timer timer;
public Person(string s)
{
Name = s;
timer = new Timer();
timer.Interval = 1000;
timer.Elapsed += timer_Elapsed;
timer.Start();
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine(Name);
timer.Dispose()
}
}
class Program
{
static void Main(string[] args)
{
Person b = new Person("张三");
b = new Person("李四");
Console.ReadKey();
}
}

gw6328 2019-05-29
  • 打赏
  • 举报
回复
恭喜楼主 设计出来了一个反对自动回收的代码。 确实这段代码刻意设计出来,这样GC不能自动回收,你也不能自动回收。所以做这种设计应该有一个开始,关闭才比较正常。
bloodish 2019-05-29
  • 打赏
  • 举报
回复
为什么没人反问楼主,要在实体类里定义一个Timer?
足球中国 2019-05-29
  • 打赏
  • 举报
回复
引用 楼主 货郎大叔 的回复:

class Person
{
    string Name;
    public Timer timer;
    public Person(string s)
    {
        Name = s;
        timer = new Timer(); 
        timer.Interval = 1000;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }
    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        Console.WriteLine(Name);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Person b = new Person("张三");
        b = new Person("李四");
        Console.ReadKey();
    }
}
上面的代码中,Person类中定义了一个定时器,当创建一个Person对象的时候,这个定时器就会启动,不断输出Name属性值。在Main函数中,给变量b赋值了一个Person对象,紧接着又重新new了一个Person对象。这样的结果是:不断输出“张三”、"李四"。那说明第一个Person对象没有被GC回收。 请问,如何销毁、回收第一个对象呢,在应用程序中,可能会遇到很多这种情况,稍不小心,就会存在对不用的对象有引用。如何回收呢?
对楼主的研究精神挺赞赏的。
闭包客 2019-05-29
  • 打赏
  • 举报
回复
IDisposable 接口,和垃圾回收机的工作是毫无关联的。 垃圾回收机只会调用类的析构函数,而不是 IDisposable 接口的方法,虽然很多人会在析构函数里面调用这些方法。
闭包客 2019-05-29
  • 打赏
  • 举报
回复
C# 是一种托管语言,托管的目的,就是为了不需要去考虑实例释放的问题。 你可以相信,只要一个实例你已经用不着了,就会被回收的。 你写的定时器,工作是永不停止的,所以没有被回收。
jwh2004 2019-05-29
  • 打赏
  • 举报
回复
将class实现IDisposable接口 class Person : System.IDisposable 并写相应的析构函数。 Person b=null; if(Person!=null) { Person.Dispose(); Person=null; } b=new Person("张三"); ...... if(Person!=null) { Person.Dispose(); Person=null; } b=new Person("李四"); 可以把创建对象再封装下。
zxy2847225301 2019-05-29
  • 打赏
  • 举报
回复
不要给Person变量b给迷惑了,当执行Person b = new Person("张三")这句代码时,变量b指向对象new Person("张三"),这时执行该对象的定时器方法,因为该定时器方法一直持有该对象的Name字段,所有当代码执行到 b = new Person("李四");变量b指向了对象new Person("李四"),而这时对象new Person("张三")已经是变成匿名对象了,而两个对象new Person("李四")和new Person("张三")都执行定时器代码,循环执行,所以两个对象都不会释放。

如果说得不对,勿喷
「已注销」 2019-05-29
  • 打赏
  • 举报
回复
Java GC会不定期对垃圾进行回收,无须像c++一样写析构函数
Shadowrabbit 2019-05-29
  • 打赏
  • 举报
回复
楼主真是写bug的天才,成功的抵制了回收gc
功夫熊猫2345 2019-05-28
  • 打赏
  • 举报
回复
在诊断工具里看,你会看到运行图谱,进程内存分配是呈现“锯齿状”的,而且每隔1秒,gc会强制释放2代对象,why?因为挂接事件本身是短程代码,而且这代码是在一个方法体类的,他又没公用出去。
  • 打赏
  • 举报
回复
实现IDispose 其实就算不实现,只要你的Person没任何引用的地方,GC也是能释放的 不信你加个GC.Collect()
wanghui0380 2019-05-28
  • 打赏
  • 举报
回复
 class Program
    {
        static void Main(string[] args)
        {
            MyClass b = new MyClass();
            b.xxxEvent += B_xxxEvent;
            b.dotest();
            Task.Run(async () =>
            {
                while (true)
                {
                      b = new MyClass();
                    b.xxxEvent += B_xxxEvent;
                    b.dotest();
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            });
          

            Console.ReadKey();

        }

       static  List<object> lst=new List<object>();
        private static void B_xxxEvent(object sender, EventArgs e)
        {
           lst.Add(sender);
        }

        class MyClass
        {
            private Guid id { get; set; }=Guid.NewGuid();

            //这里分配一个中型内存对象,以便观察gc释放情况
            public byte[] buffer=new byte[1024*40*40];

            public event EventHandler xxxEvent;
            public MyClass()
            {
           
            }

            public void dotest()
            {
                xxxEvent?.Invoke(this,new EventArgs());
            }

          
        }
    }
现在在来看,你会发现内存只增不减,why?因为那个lst还用着呢,他永不释放
wanghui0380 2019-05-28
  • 打赏
  • 举报
回复
在看另一个东西 private static void B_xxxEvent(object sender, EventArgs e) { throw new NotImplementedException(); } 我们没有实现他,但是,如果说此事件已被触发,而且这里你不是写短程代码,你写个长程代码 比如 private static void B_xxxEvent(object sender, EventArgs e) { 我把这个sender,丢到一个无限循环的处理逻辑里面,那么他虽然上面计数-1了,但是你这里计数还占着呢,所以他不释放了 } 所以我们最终讨论的不是什么,你担心的这个代码块不释放。我们最终讨论的是,你的代码是否在某个环节,永远陷入了一个“不退出”的状态,比如你的定时期,他就永远不退出。
wanghui0380 2019-05-28
  • 打赏
  • 举报
回复
一切使用证据说话,不推不猜,在vs2017下运行这个代码,在诊断工具里看,你会看到运行图谱,进程内存分配是呈现“锯齿状”的,而且每隔1秒,gc会强制释放2代对象,why?因为挂接事件本身是短程代码,而且这代码是在一个方法体类的,他又没公用出去。所以这个方法作用域内,你不用就用就是不用了,这个gc还是能看的明白的。
    class Program
    {
        static void Main(string[] args)
        {
            MyClass b = new MyClass();
            b.xxxEvent += B_xxxEvent;

            Task.Run(async () =>
            {
                while (true)
                {
                      b = new MyClass();
                    b.xxxEvent += B_xxxEvent;
                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            });
          

            Console.ReadKey();

        }

        private static void B_xxxEvent(object sender, EventArgs e)
        {
            throw new NotImplementedException();
        }

        class MyClass
        {
            private Guid id { get; set; }=Guid.NewGuid();

            //这里分配一个中型内存对象,以便观察gc释放情况
            public byte[] buffer=new byte[1024*40*40];

            public event EventHandler xxxEvent;
            public MyClass()
            {
           
            }
        

          
        }
    }
wid999 2019-05-28
  • 打赏
  • 举报
回复
引用 37 楼 货郎大叔 的回复:
[quote=引用 36 楼 wid999 的回复:]
[quote=引用 35 楼 货郎大叔 的回复:]............

太复杂的代码会吓到初学者,像你提问的这段代码就是加上timer.Dispose()。但是你得考虑到在哪实现比较合适,所以,你还是得实现一个Dispose()[/quote]
timer.Dispose()直接放到Person类中啊,person对象调用
public void AAA()
{
timer.Dispose()
}[/quote]

你终于悟了。
weixin_42176585 2019-05-28
  • 打赏
  • 举报
回复
不是很清楚,time要停止吧
加载更多回复(26)

110,536

社区成员

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

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

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