浅谈c#垃圾回收机制(GC)

写了一个window服务,循环更新sqlite记录,内存一点点稳步增长。三天后,内存溢出。于是,我从自己的代码入手,查找到底哪儿占用内存释放不掉,最终明确是调用servicestack.ormlite更新sqlite数据库造成的。至于是不是框架问题,可能性不大,因为本地模拟执行的代码没有任何问题。我觉得应该是orm在执行数据库更新后,对象还在被引用造成的。这里,我贴出一个伪代码:

//存放对象的一个列表
static List<Record> data=new List<Record>(5000); while(true){ var models = ReadDB(5000);
data.AddRange(models);
//更新model对象的字段
Dbhelp.UpdateAll(models);
data.Clear();
}

我的猜测到底对不对呢?现在还不知道。不过在探寻答案的时候,对GC的相关机制详细地了解了一遍。

一、什么是GC?

官网中有这么一句话:

The garbage collector is a common language runtime component that controls the allocation and release of managed memory。

原来GC是CLR的一个组件,它控制内存的分配与释放。

二、托管堆和CLR堆管理器

我们知道c#中的引用类型,分配在堆上。所谓的堆,就是一大块连续的内存地址。CLR堆管理器负责内存的分配、释放。堆又分为小对象堆和大对象堆。它的内存分配流程如下:

图片来源《.NET高级调试》pdf

CLR加载时,就会分配堆。

 三、GC的工作机制

 GC有三个假设:

1、如果没有特别声明,所有的对象都是垃圾(通过引用追踪对象是否为垃圾)

2、假设托管堆上所有的对象的活跃时间都是短暂的(相对于长久活跃的对象来说,GC将更频繁地收集短暂活跃的对象)

3、通过代跟踪对象的持续时间

以下是官方文档给出的和这三个假设一致

The garbage collector in the common language runtime supports object aging using generations

Objects created more recently are part of newer generations, and have lower generation numbers than objects created earlier in the application life cycle.

Objects in the most recent generation are in generation 0. This implementation of the garbage collector supports three generations of objects, generations 0, 1, and 2

每代都有自己的堆,假如0代的堆满了,就会触发GC,然后把依然有引用的对象升级,放到1代对象。最后压缩堆,把剩余的堆空间合并到一块。1代对象也是如此操作。但到了2代,就处理不同了。2代的堆可能是大对象堆,它的压缩代价过于高昂,所以只是合并相邻的空间。

图片来源博客园c#技术漫谈之垃圾回收(GC)

Garbage collection happens automatically when a request for memory cannot be satisfied using available free memory

GC发生的时机,就是相应的堆达到了阈值,因为堆也有大小限制,并不是无限的。尽管2代堆或者大对象堆满的时候,通过增加新的内存段来满足内存分配,如果没有可用的内存,这时就会报内存溢出。

四、GC不能释放非托管资源

有两种情况,第一种:托管代码引用了非托管资源,比如文件操作、数据库连接、网络连接等。这时候必须手动释放,或实现 dispose模式,或实现对象终结 。第二种:非托管代码使用了托管代码。这种情况,GC是可以回收托管对象的,因为它检测不到非托管代码的引用。

When a type uses unmanaged resources that must be released before instances of the type are reclaimed, the type can implement a finalizer.

In most cases, finalizers are implemented by overriding the Object.Finalize method; however, types written in C# or C++ implement destructors, which compilers turn into an override of Object.Finalize

必须注意的一点是,实现对象终结器,GC会在释放对象之前自动调用。其实这是一个代价非常高昂的备用机制。所以能自己释放非托管资源的,就自己释放。

如果一个对象中包含有终结器,那么在new的时候放入到终结者队列。当GC会把这个对象标为垃圾时,放入到另一个队列F-Reachable中。这个队列包含了所有带有终结器并且将被作为垃圾收集的对象,这些对象的终结器都将被执行。在垃圾收集的过程总并不会执行终结器代码。而是由.NET 进程的终结线程调用。因此,此时的垃圾回收滞后一段时间,目的在于等待终结器代码执行的完成。

五、dispose模式

 1 using System;
2
3 class BaseClass : IDisposable
4 {
5 // Flag: Has Dispose already been called?
6 bool disposed = false;
7
8 // Public implementation of Dispose pattern callable by consumers.
9 public void Dispose()
10 {
11 Dispose(true);
12 GC.SuppressFinalize(this);
13 }
14
15 // Protected implementation of Dispose pattern.
16 protected virtual void Dispose(bool disposing)
17 {
18 if (disposed)
19 return;
20
21 if (disposing) {
22 // Free any other managed objects here.
23 //
24 }
25
26 // Free any unmanaged objects here.
27 //
28 disposed = true;
29 }
30
31 ~BaseClass()
32 {
33 Dispose(false);
34 }
35 }
 1 using Microsoft.Win32.SafeHandles;
2 using System;
3 using System.Runtime.InteropServices;
4
5 class DerivedClass : BaseClass
6 {
7 // Flag: Has Dispose already been called?
8 bool disposed = false;
9 // Instantiate a SafeHandle instance.
10 SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
11
12 // Protected implementation of Dispose pattern.
13 protected override void Dispose(bool disposing)
14 {
15 if (disposed)
16 return;
17
18 if (disposing) {
19 handle.Dispose();
20 // Free any other managed objects here.
21 //
22 }
23
24 // Free any unmanaged objects here.
25 //
26
27 disposed = true;
28 // Call base class implementation.
29 base.Dispose(disposing);
30 }
31 }

这是基类和子类的dispose模式,来源于官网。

C#写的window服务内存溢出的更多相关文章

  1. 解决eclipse中启动服务内存溢出问题

    在eclipse中双击tomcat进入参数配置页面,点击Open Launch configuration---Arguments,在VM Arguments的末尾加 -Xms1024M -Xmx20 ...

  2. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  3. JVM内存溢出环境备份方法

    线上Tomcat服务内存溢出,且不容易重现,又没配置JMX监控端口,如何在不重启Tomcat的情况下备份堆dump和线程dump,进而分析原因? 因为Tomcat以服务模式运行,直接用JVisualV ...

  4. Redis采坑(一)——数据无法插入,内存溢出

    一.采坑背景 在最大数据分析的过程中,redis是被当做热数据的缓存库使用的,在某一天中,redis数据库热数据无法插入,此时数据量大概在100万左右,很是纠结,为什么不能插入?程序的错误,不可能,没 ...

  5. 一次apk打开时报内存溢出错误,故写下内存溢出的各种原因和解决方法

    原转载:https://blog.csdn.net/cp_panda_5/article/details/79613870 正文内容: 对于JVM的内存写过的文章已经有点多了,而且有点烂了,不过说那么 ...

  6. 用http请求thrift服务端出现了内存溢出的情况

    记一次内存溢出的分析经历 - Janti - 博客园 https://www.cnblogs.com/superfj/p/8474288.html 说在前面的话 朋友,你经历过部署好的服务突然内存溢出 ...

  7. JVM内存溢出后服务还能运行吗

    文章开篇问一个问题吧,一个java程序,如果其中一个线程发生了OOM,那进程中的其他线程还能运行吗? 接下来做实验,看看JVM的六种OOM之后程序还能不能访问. 在这里我用的是一个springboot ...

  8. 老李案例分享:MAT分析应用程序服务出现内存溢出过程

    老李案例分享:MAT分析应用程序服务出现内存溢出过程   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loa ...

  9. window下tomcat的内存溢出问题

    打开注册表:https://jingyan.baidu.com/article/49ad8bce09d6085835d8fa63.html Tomcat 内存溢出对应解决方式 Windows平台,使用 ...

随机推荐

  1. Python基础--列表、元组

    一.什么是列表.元组 序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内置类型,但最常见 ...

  2. Couchbase集群

    Couchbase集群 http://www.cnblogs.com/sunwubin/p/3426801.html Couchbase服务器可以单独运行,也可以作为集群运行.在Couchbase集群 ...

  3. zzuli1985(dp/水dfs郑轻比赛)

    再一次感受到dp的威力 1985: 即将到来的新生赛 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 112  Solved: 28 SubmitStat ...

  4. Oracle 11g 物理Dataguard日常操作维护(二)

    Oracle 11g 物理Dataguard日常操作维护(二) 2017年8月25日 14:34 3.3 3.3.1 查看备库进程状态 SYS(125_7)@fpyj123> select pr ...

  5. 双机热备(准)-->RAC(夭折)-->DG(异地容灾)

    以下有的地方为oracle专业术语,非懂勿喷.前段时间某项目负责人告知,他们应用需要一套oracle数据库环境运行模式为双机热备.简单了解下对于现在已经非常成熟的RAC再合适不过了.详细问了问当前服务 ...

  6. ajax中文乱码问题的总结

    ajax中文乱码问题的总结 2010-12-11 22:00 5268人阅读 评论(1) 收藏 举报 ajaxurljavascriptservletcallback服务器 本章解决在AJAX中常见的 ...

  7. 5.4 使用 Razor 表达式

    以下内容主要展示 Razor 所支持的各种表达式,以及如何用它们来创建视图的内容. 在一个好的 MVC 框架应用程序中,动作方法与视图的作用是清晰.分离的.其规则很简单,如表所示: 组件 要做的事 不 ...

  8. (转)使用UTL_SMTP包发送邮件

    使用UTL_SMTP包发送邮件 参考文档 Email From Oracle PL/SQL (UTL_SMTP) UTL_SMTP Oracle 存储过程中发送邮件,并支持用户验证.中文标题和内容

  9. quartz---(1)

    Quartz Quartz是什么? 简单的一些例子 Quartz是什么 简单的认为Quartz是是一个定时器. 1.1. 下载 http://www.quartz-scheduler.org/down ...

  10. IOS UI总结

    一.UIView常见属性 1.frame  位置和尺寸(以父控件的左上角为原点(0,0)) 2.center 中点(以父控件的左上角为原点(0,0)) 3.bounds  位置和尺寸(以自己的左上角为 ...