试着把.net的GC讲清楚(1)
什么是GC?
GC(garbage collection)是对内存管理中回收已经不用的内存的一种机制,我们熟知的java和.net都有自己的GC机制,是内存管理的一部分。
为什么会有GC呢?是因为动态的内存分配和分布操作系统是不管的,得各类语言自己实现,例如c和c++自己需要手动管理分配的内存资源,如果不手动释放,那么会造成已经无用的内存不能被操作系统识别使用,也就是所谓的内存泄漏。
.net的GC都是发生在堆(heap),因为这个动态的内存是在堆上分配的。为什么.net 没有像c++一样提供手动管理内存的操作?因为手工管理内存非常容易出问题,开发人员不应花费时间在这个上面,避免人为问题,就找一个管理内存的“人”来,处理这些事情,于是GC就出现了(说一句话,以前自己是c、c++出身,真的一个语言影响一个人的知识广度和深度),大家都不用考虑这些事情,集中在重要的事情上,就像自己找了个管家,而且是专业的,不会出错的那种,省心。
GC有哪些分类?
我了解到的有:Reference Count、Mark and Sweep(升级版Mark and Compact)、Copy and Collection
现在java和.net 使用的是Mark and Compact算法,这个算法是从Mark and Sweep算法演变过来的,下来就讲讲Mark and Sweep。
Mark and Sweep:分为两个阶段,第一阶段标记所有现在还可以使用的对象,第二阶段清除标记的对象之外的内存。
在.net中,GC管理了一组root(由全局对象组成),通过遍历所有的root机器引用的子对象,进行内存中的存活对象的标记,之后就清除未标记为存活的对象。这就是Mark and Sweep算法,但是这个造成了一个问题,就是回收后的内存是成了筛子了,这个时候如果来一个大的对象需要分配内存,那么空余内存总额大于分配对象的大小,但是找不到一个连续的可以容下这个对象大小的内存,这个时候怎么办?其实模拟操作系统,再做一个内存管理的机制就行,在逻辑上看着连续就行了。当然这个不是本次讨论的对象,Mark and Compact解决了内存不连续的问题,因为它把内存做了一次整理(把不相邻的内存移动到一块,看着就连续了)
Mark and Compact:在Mark and Sweep基础上做了一次内存整理,因为内存做整理的时候,对象的引用是不能被使用的,引用地址会变,所以啊,GC的时候,使用到这些对象的线程什么的是会被挂起等待的,也不能经常回收内存,不然性能堪忧,就是因为回收导致挂起了。
啥是0代、1代、2代对象?
要解释这个问题,还得从内存回收时间说起,这里有个假设(其实也是规则)回收内存中所有对象的时间大于回收部分对象的时间,于是就把内存中对象分成了几代,0代对象指最新分配内存的对象,一次类推。其实多少代,这个由GC决定,.net中GC中代数是3代(这个值暂时不能确定能不能改)。
GC怎么管理代对象呢?一般情况下,分配的对象都是0代对象,在分配对象内存时,如果0代对象的内存已经不能容纳新对象了(超过0代对象内存的上限),在gc回收一次0代后,这个还存活的对象代数加1(GC.Collect();GC.GetGeneration(obj)
代码验证过,现在不清楚自动触发是不是回收一次加1),同理如果1代对象超过了1代内存的上限,也会触发gc回收1代对象。那么这个内存回收是定是这样的么?不一定,毕竟微软提供了手动触发gc的功能,就是GC.Collect()
,有兴趣可以翻翻这个方法。
代数的大小,查了很多资料之后,只发现一篇文章说到,.net中0代和1代之和为16MB,2代内存上限非常的大,具体有framework版本和其他一些因素决定的。
//验证回收一次,对象就升一代
Object obj=new Object();
Console.WriteLine(GC.GetGeneration(obj));
GC.Collect();
Console.WriteLine(GC.GetGeneration(obj));
GC.Collect();
Console.WriteLine(GC.GetGeneration(obj));
Finalize、Dispose是啥?如何理解?
.net中有托管资源和非托管资源的分类,托管资源.net自己就可以管理,非托管资源,需要特殊的方法,也就是托管资源在GC的时候,.net可以自己识别,但是非托管资源,GC是自动释放不了的。
什么是非托管资源?这让我想起之前用mfc写windows程序的时候,什么画刷、画笔、com之类的,就是非托管资源,还有数据库连接、文件、套接字之类的也是,哦,还有流之类的都是。
这些非托管资源,一般都需要自己释放资源,.net提供了IDsiposable的接口,实现这个接口的方法,在里面进行资源释放,使用using语句来简化这个非托管资源的释放工作。
Finalize:这个也能用来释放非托管资源,与IDsiposable接口区别是,它的调用时机是不定的,因为它是由GC调用的,GC调用真的不定的,因为调用一次GC调用会降低程序性能(前面说的,内存压缩导致引用需要变化,而因为线程挂起),下面来说说为什么它是由GC调用的。
在创建对象的时候,会把还有析构函数(编译之后,就是Finalize方法,与c++中的析构函数不同)的对象引用存到一个叫做Finalizer Queue的list中,在GC的时候,如果一个对象是无用的,而且在Finalizer Queue里面有引用,此次并不回收,并且会把引用从Finalizer Queue移到Freachable Queue的list中,Freachable Queue的list有内容之后会启动一个线程,然后执行里面的引用的对象的析构函数,执行完毕后把对象的引用删除,等待下次GC的时候,才进行回收此对象。
所以Finalize的特点就是:
- 啥时候调用不定
- 这类对象,需要至少两次GC才能回收。
为什么至少两次,而不是两次,因为.net为我们提供了一个把对象引用放回Finalizer Queue的方法,GC.ReRegisterForFinalize(),如果在Finalize中调用了这个代码,那么就死不了了。
微软不建议使用Finalize方法,就像其他博客中提到的,我们可以把它留作后手,万一那个非托管资源该释放没有释放,可以在Finalize方法中做为最后的保险(算是避免人为原因)。
我确实释放完了非托管资源,就是不想执行Finalize方法,微软也提供了方法了:GC.SuppressFinalize(this),这个方法执行了之后就把这个对象的引用中Finalizer Queue移除了。
LOH是什么?
LOH(large object heap)是为了大对象而专门设计的一个堆,多大的对象会分配到这个堆里面?超过85000个字节的就会。其实这个loh产生原因大对象移动非常的耗时,还不如不移动,例如,3个对象ABC,AB对象大约占个80个字节,C对象占个10000个字节,假设AB对象被回收,那么在移动阶段,就要把10000个字节,往前移动80字节,还不如不移动性能高。这个85000字节也是一个经验值。
既然loh不能移动,那么肯定不能用Mark and Compact中的移动了(使用什么算法现在还不清楚,猜测是Mark and Sweep,或许是特例),并且在只有2代对象回收的时候才进行回收。
GC模式?
- workstation mode:用于单处理器的系统中,频繁回收,从而阻止一次长时间的回收对程序的挂起时间。
- server mode:用于多处理器的系统中,为每个处理器都创建一个GC Heap,该模式特点是,分配内存较大,能不回收就不回收,回收时候耗时太长。
其中有Concurrent GC 工作方式,其中workstation mode和server mode都可以配置,在单处理器上设置为true也不生效,主要用于用户线程在gc时候可以大部分时间和gc线程并发,详细可以参考:https://blogs.msdn.microsoft.com/seteplia/2017/01/05/understanding-different-gc-modes-with-concurrency-visualizer/
啥时候需要手动gc?
资源特别紧张的时候,例如之前面试的一家公司,系统是在azure上,内存什么的特别贵,这个时候手动gc可能其中一种手段了。
最后
其实gc的最耗时还是在算法的选择上,比如Mark and Compact中的把内存合并成连续的,这个才是耗时的,如果内存足够多,根本就不需要考虑移动内存。
或者像我之前提的,再在内存上面做一次内存映射的管理,也可以避免内存不连续的问题,当然肯定会遇到各种各样的问题。
试着把.net的GC讲清楚(1)的更多相关文章
- 试着把.net的GC讲清楚(2)
试着把.net的GC讲清楚(1) 上篇文章说了一些基本概念的东西,然后还有很多东西概念没有头绪,这篇文章我试着解释 GC的回收算法详细步骤? 上篇]文章讲了.net GC的算法是Mark and Co ...
- 试着把.net的GC讲清楚(3)
前两篇写的都是gc的一些概念和细节,这些东西对自己以后写代码有什么用,本篇我就准备将这些内容. root 第一篇文章中讲了GC在遍历存活对象的时候,都是从root开始的,root是一些对象的引用,例如 ...
- 《你不知道的JavaScript下卷》知识点笔记
1. [1, 2, 3] == [1, 2, 3] 返回false [1, 2, 3] == “1, 2, 3” 返回true 2. 如果 < 比较的两个值都是字符串, 就像在b < c中 ...
- 试着讲清楚:js代码运行机制
一. js运行机制 js执行引擎 经常看文章的说到js是带线程的,其实这个说法非常的模糊,准确的是js执行引擎是单线程的,js执行引擎就是js代码的执行器,有了这个概念就可以下来说说js是如何运行的了 ...
- 深入浅出聊优化:从Draw Calls到GC
前言: 刚开始写这篇文章的时候选了一个很土的题目...<Unity3D优化全解析>.因为这是一篇临时起意才写的文章,而且陈述的都是既有的事实,因而给自己“文(dou)学(bi)”加工留下的 ...
- PHP新的垃圾回收机制:Zend GC详解
概述 在5.2及更早版本的PHP中,没有专门的垃圾回收器GC(Garbage Collection),引擎在判断一个变量空间是否能够被释放的时候是依据这个变量的zval的refcount的值,如果re ...
- GitHub上那些值得一试的JAVA开源库--转
原文地址:http://www.jianshu.com/p/ad40e6dd3789 作为一名程序员,你几乎每天都会使用到GitHub上的那些著名Java第三方库,比如Apache Commons,S ...
- 转:面试题:“你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?”
转自:http://jeromecen1021.blog.163.com/blog/static/18851527120117274624888/ 面试题目: 地球人都知道,Java有个东西叫垃圾收集 ...
- 优化 从Draw Calls到GC
原文出处: 慕容小匹夫的博客(@慕容小匹夫) 欢迎分享原创到伯乐头条 前言: 刚开始写这篇文章的时候选了一个很土的题目...<Unity3D优化全解析>.因为这是一篇临时起意才写的文章 ...
随机推荐
- .28-浅析webpack源码之compiler.resolvers
原本该在过WebpackOptionsApply时讲解这个方法的,但是当时一不小心过掉了,所以在这里补上. compiler.resolvers 该对象的三个方法均在WebpackOptionsApp ...
- Android 通知 相关api记录
记录帖 通知(Notification) 的API 1.使用getSystemService()方法获取系统服务,参数接收一个字符串来确定获取具体的服务,使用通知传入Content.NOTIFICAT ...
- Sqoop介绍
Sqoop介绍 http://sqoop.apache.org http://sqoop.apache.org/docs/1.4.6/SqoopUserGuide.html 1.什么是Sqoop? ...
- 分享给有需要的你,精选了10套web开发免费视频教程~~
在这里给大家精选百度传课里的一些比较好的视频教程,比较适合新手们,有需要的可以收藏着学习!! 蓝鸥微信小程序全方位深度解析 链接:http://www.chuanke.com/v4702151-19 ...
- sqlite效率探测
在编译后当然就是使用sqlite,贸贸然去测试了一下创建数据库,插入数据,仅几条数据,发现,真不错的数据库,后来把数据量提高到10000的 时候,发现,怎么这么慢,后来自己都受不了,网上一查,原来是如 ...
- zTree中父节点禁用,子节点可以用
参考学习网址:http://www.treejs.cn/v3/main.php#_zTreeInfo zTree中父节点禁用,子节点可以用 axios.get('/base/unit/unittree ...
- struts文件异常Included file cannot be found
1.命名规范,都是采用struts-xxx.xml文件,即以struts开头 2.file的路径不要以/开头,在其他版本是以/开头的 <include file="/com/bjsxt ...
- cookie sessionStorage localStorage 之间的关系
先说一个cookie 因为HTTP是无状态的 所以cookie诞生 用于保存会话信息 大小 4096b 一般在4095b以内 数量限制 20 -50 根据浏览器不同 操作的是一个字符串 可以设置参数 ...
- Windows核心编程&进程
1. 进程的定义 说白了进程就是一个正在运行的执行程序,包含内核对象和独立的地址空间,内核对象负责统计和管理进程信息,地址空间包括所有可执行文件或DLL 模块的代码和数据.动态内存分配(线程堆和栈的分 ...
- ClearCase新增文件
原文地址:http://blog.csdn.net/ace_fei/article/details/7531376 大家应该都知道在clearcase上新增文件是通过以下过程来生成的: clearto ...