大家好,如果您还对把网站源码分享导入win2008不太了解,没有关系,今天就由本站为大家分享把网站源码分享导入win2008的知识,包括网站源码文件的问题都会给大家分析到,还望可以解决大家的问题,下面我们就开始吧!
什么是.NET?什么是.NETFramework?本文将从上往下,循序渐进的介绍一系列相关.NET的概念,先从类型系统开始讲起,我将通过跨语言操作这个例子来逐渐引入一系列.NET的相关概念,这主要包括:CLS、CTS(CLI)、FCL、Windows下CLR的相关核心组成、Windows下托管程序运行概念、什么是.NETFramework,.NETCore,.NETStandard及一些VS编译器相关杂项和相关阅读链接。完整的从上读到下则你可以理解个大概的.NET体系。
文章是我一字一字亲手码出来的,每天下班用休息时间写一点,持续了二十来天。且对于文章上下衔接、概念引入花了很多心思,致力让很多概念在本文中显得通俗。但毕竟.NET系统很庞大,本文篇幅有限,所以在部分小节中我会给出延伸阅读的链接,在文章结尾我给出了一些小的建议,希望能对需要帮助的人带来帮助,如果想与我交流可以文章留言或者加.NET技术交流群:166843154
.NET和C1.0正式版。
只要是.NET支持的编程语言,开发者就可以通过.NET平台提供的工具服务和框架支持便捷的开发应用程序。
C编程语言为示例,所以经常就.NET和C、VisualBasic、C++/CLI、Eiffel、F和VB来演示什么是.NET中的跨语言互操作性。
通俗来说,虽然c写的类可以在vb中当做自家写的类一样正常使用。
比如我在vb中写了一个针对String的首字母大写的扩展方法,将其编译后的dll引用至C项目中,可以像自身代码一样正常使用来自vb这个dll的扩展方法。
现在有那么多面向对象语言,但不是所有编程语言都能这样直接互操作使用,而.NET平台支持的C中定义了一个基类,类里面包含一个公开的指针类型的成员,我想在vb中继承这个类,并访问这个公开的成员。
但是vb语言因为其定位不需要指针,所以并没有C语言中,对类名是区分大小写的,我在C的程序集中避免这些因语言的差异性而导致的错误,在编写c和VB,我还想我编写的代码在.Net平台上通用的话,那么我还必须得知道.NET平台支持的每一种语言和我编写代码所使用的语言的差异,从而在编写代码中避免这些。
这几年编程语言层出不穷,在将来.NET可能还会支持更多的语言,如果说对一个开发者而言掌握所有语言的差异处这是不现实的,所以.NET专门为此参考每种语言并找出了语言间的共性,然后定义了一组规则,开发者都遵守这个规则来编码,那么代码就能被任意.NET平台支持的语言所通用。
而与其说是规则,不如说它是一组语言互操作的标准规范,它就是公共语言规范-CommonLanguageSpecification,简称CLS
CLS从类型、命名、事件、属性、数组等方面对语言进行了共性的定义及规范。这些东西被提交给欧洲计算机制造联合会ECMA,称为:共同语言基础设施。
就以类型而言,CLS定义了在C中的char*指针类型,即使vb中没有char*这种等价的指针类型,但mscorlib提供了针对指针类型的Pointer包装类供其访问,可以从运行时类携带的类型名称看到其原本的类型名。
可以看到,该类中的元素是不符合CLS规范的。
CLS异常
提到特殊情况,还要说的一点就是异常处理。.NET框架组成中定义了异常类型系统,在编译器角度,所有catch捕获的异常都必须继承自System.Exception,如果你要调用一个由不遵循此规范的语言抛出其它类型的异常对象(C++允许抛出任何类型的异常,如C2.0之前Catch(Exception)是捕捉不了的,但之后的版本可以。
在后续版本中,微软提供了System.Runtime.CompilerServices.RuntimeWrappedException异常类,将那些不符合CLS的包含Exception的对象封装起来。并且可以通过RuntimeCompatibilityAttribute特性来过滤这些异常。
RuntimeWrappedException:https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.compilerservices.runtimewrappedexception?view=netframework-4.7.2
那么,这个段落总结一下,什么是CLS呢?
在面向.NET开发中,编写跨语言组件时所遵循的那些共性,那些规范就叫做CommonLangrageSpecification简称CLS,公共语言规范
什么是CTS?
如果理解了什么是CLS的话,那么你将很轻松理解什么是CTS。
假设你已经围绕着封装继承多态这3个特性设计出了多款面向对象的语言,你发现大家都是面向对象,都能很好的将现实中的对象模型表达出来。除了语法和功能擅长不同,语言的定义和设计结构其实都差不多一回事。
比如,现实中你看到了一辆小汽车,这辆车里坐着两个人,那么如何用这门语言来表达这样的一个概念和场面?
首先要为这门语言横向定义一个“类型”的概念。接下来在程序中就可以这样表示:有一个汽车类型,有一个人类型,在一个汽车类型的对象内包含着两个人类型的对象,因为要表达出这个模型,你又引入了“对象”的概念。而现在,你又看到,汽车里面的人做出了开车的这样一个动作,由此你又引入了“动作指令”这样一个概念。
接着,你又恍然大悟总结出一个定理,无论是什么样的“类型”,都只会存在这样一个特征,即活着的带生命特征的(如人)和死的没有生命特征的(如汽车)这两者中的一个。最后,随着思想模型的成熟,你发现,这个“类型”就相当于一个富有主体特征的一组指令的集合。
好,然后你开始照葫芦画瓢。你参考其它程序语言,你发现大家都是用class来表示类的含义,用struct表示结构的含义,用new来表示新建一个对象的含义,于是,你对这部分功能的语法也使用class和new关键字来表示。然后你又发现,他们还用很多关键字来更丰富的表示这些现实模型,比如override、virtual等。于是,在不断的思想升级和借鉴后,你对这个设计语言过程中思想的变化仔细分析,对这套语言体系给抽象归纳,最终总结出一套体系。
于是你对其它人这样说,我总结出了一门语言很多必要的东西如两种主要类别:值类别和引用类别,五个主要类型:类、接口、委托、结构、枚举,我还规定了,一个类型可以包含字段、属性、方法、事件等成员,我还指定了每种类型的可见性规则和类型成员的访问规则,等等等等,只要按照我这个体系来设计语言,设计出来的语言它能够拥有很多不错的特性,比如跨语言,跨平台等,C来编写的。
在FCL中,除了最基础的那部分BCL之外,还包含我们常见的如:用于网站开发技术的ASP.NET类库,该子类包含webform/webpage/mvc,用于桌面开发的WPF类库、WinForm类库,用于通信交互的WCF、asp.netwebapi、WebService类库等等
什么是基元类型?
像上文在CTS中提到了基本基元数据类型,大家知道,每门语言都会定义一些基础的类型,比如C就用int关键字来表示System.Int32,用string关键字来表示System.String等,所以我们才能这样去写。
像这样被表述于编译器直接支持的类型叫做基元类型,它被直接映射于BCL中具体的类。
下面是部分面向.NET的语言的基元类型与对应的BCL的类别图:
System.Object的意义
说起类型,这里要说CTS定义的一个非常重要的规则,就是类与类之间只能单继承,System.Object类是所有类型的根,任何类都是显式或隐式的继承于System.Object。
System.Object定义了类型的最基本的行为:用于实例比较的Equals系列方法、用于Hash表中Hash码的GetHashCode、用于Clr运行时获取的类型信息GetType、用于表示当前对象字符串的ToString、用于执行实例的浅复制MemberwiseClone、用于GC回收前操作的析构方法Finalize这6类方法。
所以Object不仅是C那样全面的,我们只需明记一点:对于符合CTS的那部分自然就按照CTS定义的规则来。任何可遵循CTS的类型规范,同时又有.NET运行时的实现的编程语言就可以成为.NET中的一员。
计算机是如何运行程序的?
接下来我要说什么是.NET的跨平台,并解释为什么能够跨语言。不过要想知道什么是跨平台,首先你得知道一个程序是如何在本机上运行的。
什么是CPU
CPU,全称CentralProcessingUnit,叫做中央处理器,它是一块超大规模的集成电路,是计算机组成上必不可少的组成硬件,没了它,计算机就是个壳。
无论你编程水平怎样,你都应该先知道,CPU是一台计算机的运算核心和控制核心,CPU从存储器或高速缓冲存储器中取出指令,放入指令寄存器,并对指令译码,执行指令。
我们运行一个程序,CPU就会不断的读取程序中的指令并执行,直到关闭程序。事实上,从电脑开机开始,CPU就一直在不断的执行指令直到电脑关机。
什么是高级编程语言
在计算机角度,每一种CPU类型都有自己可以识别的一套指令集,计算机不管你这个程序是用什么语言来编写的,其最终只认其CPU能够识别的二进制指令集。
在早期计算机刚发展的时代,人们都是直接输入01010101这样的没有语义的二进制指令来让计算机工作的,可读性几乎没有,没人愿意直接编写那些没有可读性、繁琐、费时,易出差错的二进制01代码,所以后来才出现了编程语言。
编程语言的诞生,使得人们编写的代码有了可读性,有了语义,与直接用01相比,更有利于记忆。
而前面说了,计算机最终只识别二进制的指令,那么,我们用编程语言编写出来的代码就必须要转换成供机器识别的指令。
就像这样:
code:1+2function翻译方法(参数:code)
{
…
“1”=>”001″;
“2”=>”002″;”+”=>”000″;
return能让机器识别的二进制代码;
}
call翻译方法(“1+2″)=>”001000002”
所以从一门编程语言所编写的代码文件转换成能让本机识别的指令,这中间是需要一个翻译的过程。
而我们现在计算机上是运载着操作系统的,光翻译成机器指令也不行,还得让代码文件转化成可供操作系统执行的程序才行。
那么这些步骤,就是编程语言所对应的编译环节的工程了。这个翻译过程是需要工具来完成,我们把它叫做编译器。
不同厂商的CPU有着不同的指令集,为了克服面向CPU的指令集的难读、难编、难记和易出错的缺点,后来就出现了面向特定CPU的特定汇编语言,比如我打上这样的x86汇编指令movax,bx,然后用上用机器码做的汇编器,它将会被翻译成1000100111011000这样的二进制01格式的机器指令.
不同CPU架构上的汇编语言指令不同,而为了统一一套写法,同时又不失汇编的表达能力,C语言就诞生了。
用C语言写的代码文件,会被C编译器先转换成对应平台的汇编指令,再转成机器码,最后将这些过程中产生的中间模块链接成一个可以被操作系统执行的程序。
那么汇编语言和C语言比较,我们就不需要去阅读特定CPU的汇编码,我只需要写通用的C源码就可以实现程序的编写,我们用将更偏机器实现的汇编语言称为低级语言,与汇编相比,C语言就称之为高级语言。
在看看我们C等这样的高级语言来说是不区分平台的,而在于其背后支持的这个翻译原理是否能支持其它平台。
什么是托管代码,托管语言,托管模块?
作为一门年轻的语言,C则更为高级。
往往一段简小的C语言你几乎不需要指针的使用,这也就意味着你几乎不需要进行人为的内存管控与安全考虑因素,也不需要多懂一些操作系统的知识,这让编写程序变得更加轻松和快捷。
如果说C编程不需要人为内存管控是怎么做到的呢?
.NET提供了一个垃圾回收器(GC)来完成这部分工作,当你创建类型的时候,它会自动给你分配所需要的这部分内存空间。就相当于,有一个专门的软件或进程,它会读取你的代码,然后当你执行这行代码的时候,它帮你做了内存分配工作。这部分本该你做的工作,它帮你做了,这就是“托管”的概念。比如现实中托管店铺、托管教育等这样的别人替你完成的概念。
因此,C编写的代码也就称之为托管代码,C角度,那些脱离了.NET提供的诸如垃圾回收器这样的环境管制,就是对应的非托管了。
非托管的异常
我们编写的程序有的模块是由托管代码编写,有的模块则调用了非托管代码。在.NETFramework中也有一套基于此操作系统SEH的异常机制,理想的机制设定下我们可以直接通过catch(e)或catch来捕获指定的异常和框架设计人员允许我们捕获的异常。
而异常类型的级别也有大有小,有小到可以直接框架本身或用代码处理的,有大到需要操作系统的异常机制来处理。.NET会对那些能让程序崩溃的异常类型给进行标记,对于这部分异常,在.NETFramework4.0之前允许开发人员在代码中自己去处理,但4.0版本之后有所变更,这些被标记的异常默认不会在托管环境中抛出(即无法catch到),而是由操作系统的SEH机制去处理。
不过如果你仍然想在代码中捕获处理这样的异常也是可以的,你可以对需要捕获的方法上标记[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptionsAttribute]特性,就可以在该方法内通过catch捕获到该类型的异常。你也可以通过在配置文件中添加运行时节点来对全局进行这样的一个配置:
HandleProcessCorruptedStateExceptions特性:https://msdn.microsoft.com/zh-cn/library/azure/system.runtime.exceptionservices.handleprocesscorruptedstateexceptionsattribute.aspx
SEHException类:https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.sehexception(v=vs.100).aspx
处理损坏状态异常博客专栏:https://msdn.microsoft.com/zh-cn/magazine/dd419661.aspx
什么是CLR,.NET虚拟机?
实际上,.NET不仅提供了自动内存管理的支持,他还提供了一些列的如类型安全、应用程序域、异常机制等支持,这些都被统称为CLR公共语言运行库。
CLR是.NET类型系统的基础,所有的.NET技术都是建立在此之上,熟悉它可以帮助我们更好的理解框架组件的核心、原理。
在我们执行托管代码之前,总会先运行这些运行库代码,通过运行库的代码调用,从而构成了一个用来支持托管程序的运行环境,进而完成诸如不需要开发人员手动管理内存,一套代码即可在各大平台跑的这样的操作。
这套环境及体系之完善,以至于就像一个小型的系统一样,所以通常形象的称CLR为”.NET虚拟机”。那么,如果以进程为最低端,进程的上面就是.NET虚拟机(CLR),而虚拟机的上面才是我们的托管代码。换句话说,托管程序实际上是寄宿于.NET虚拟机中。
什么是CLR宿主进程,运行时主机?
那么相对应的,容纳.NET虚拟机的进程就是CLR宿主进程了,该程序称之为运行时主机。
这些运行库的代码,全是由C/C++编写,具体表现为以mscoree.dll为代表的核心dll文件,该dll提供了N多函数用来构建一个CLR环境,最后当运行时环境构建完毕(一些函数执行完毕)后,调用_CorDllMain或_CorExeMain来查找并执行托管程序的入口方法(如控制台就是Main方法)。
如果你足够熟悉CLR,那么你完全可以在一个非托管程序中通过调用运行库函数来定制CLR并执行托管代码。
像SqlServer就集成了CLR,可以使用任何.NETFramework语言编写存储过程、触发器、用户定义类型、用户定义函数(标量函数和表值函数)以及用户定义的聚合函数。
有关CLR大纲介绍:https://msdn.microsoft.com/zh-cn/library/9x0wh2z3(v=vs.85).aspx
CLR集成:https://docs.microsoft.com/zh-cn/previous-versions/sql/sql-server-2008/ms131052(v%3dsql.100)
构造CLR的接口:https://msdn.microsoft.com/zh-cn/library/ms231039(v=vs.85).aspx
适用于.NETFramework2.0的宿主接口:https://msdn.microsoft.com/zh-cn/library/ms164336(v=vs.85).aspx
选择CLR版本:https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/startup/supportedruntime-element
所以C语言的命令行形式的编译器csc.exe和用于VB语言的命令行形式的编译器vbc.exe。
我们通过编译器可以将后缀为.cs(C格式文件,2.是代码语法等检测分析必须正确,3.是使用的类库必须有出处(引用的dll),当然因为我是编译为控制台程序,所以还必须得有个静态Main方法入口,以上缺一不可。
可以看出,这段命令我是将位于D:\\dic\\的demo.cs文件给编译成位于D:\\名为demo.exe的控制台文件,并且因为在代码中使用到了System.dll,所以还需要通过/r注册该元数据表。
这里得注意为什么没有/r:mscorlib.dll,因为mscorlib.dll地位的特殊,所以csc总是对每个程序集进行mscorlib.dll的注册(自包含引用该dll),因此我们可以不用/r:mscorlib.dll这个引用命令,但为了演示效果我还是决定通过/nostdlib命令来禁止csc默认导入mscorlib.dll文件。
所以,最终命令是这样的:cscD:\\dic\\demo.cs/r:D:\\dic\\mscorlib.dll/r:D:\\dic\\System.dll/nostdlib
因为没有指定输出文件/out选项,所以会默认输出在与csc同一目录下名为demo.exe的文件。事实上,在csc的命令中,如果你没有指定路径,那么就默认采用在csc.exe的所在目录的相对路径。
而我们可以看到,在该目录下有许多程序集,其中就包含我们需要的System.dll和mscorlib.dll,所以我们完全可以直接/r:mscorlib.dll/r:System.dll
而类似于System.dll、System.Data.dll这样使用非常频繁的程序集,我们其实不用每次编译的时候都去手动/r一下,对于需要重复劳动的编译指令,我们可以将其放在后缀为.rsp的指令文件中,然后在编译时直接调用文件即可执行里面的命令@{name}.rsp。
csc.exe默认包含csc.rsp文件,我们可以用/noconfig来禁止默认包含,而csc.rsp里面已经写好了我们会经常用到的指令。
所以,最终我可以这样写cscD:\\dic\\demo.cs直接生成控制台应用程序。
.NET程序执行原理
好的,现在我们已经有了一个demo.exe的可执行程序,它是如何被我们运行的?。
C源码第一行代码:stringrootDirectory=Environment.CurrentDirectory;被翻译成IL代码:callstring[mscorlib/*23000001*/]System.Environment/*01000004*/::get_CurrentDirectory()/*0A000003*/
这句话意思是调用System.Environment类的get_CurrentDirectory()方法(属性会被编译为一个私有字段+对应get/set方法)。
点击视图=>元信息=>显示,即可查看该程序集的元数据。
我们可以看到System.Environment标记值为01000004,在TypeRef类型引用表中找到该项:
注意图,TypeRefName下面有该类型中被引用的成员,其标记值为0A000003,也就是get_CurrentDirectory了。
而从其ResolutionScope指向位于0x23000001而得之,该类型存在于mscorlib程序集。
于是我们打开mscorlib.dll的元数据清单,可以在类型定义表(TypeDef)找到System.Environment,可以从元数据得知该类型的一些标志(Flags,常见的public、sealed、class、abstract),也得知继承(Extends)于System.Object。在该类型定义下还有类型的相关信息,我们可以在其中找到get_CurrentDirectory方法。我们可以得到该方法的相关信息,这其中表明了该方法位于0x0002b784这个相对虚地址(RVA),接着JIT在新地址处理CIL,周而复始。
元数据在运行时的作用:https://docs.microsoft.com/zh-cn/dotnet/standard/metadata-and-self-describing-components是一种强类型语言,每个变量和常量都有一个类型,在.NET中,每种类型又被定义为值类型或引用类型。
使用struct、enum关键字直接派生于System.ValueType定义的类型是值类型,使用class、interface、delagate关键字派生于System.Object定义的类型是引用类型。
对于在一个方法中产生的值类型成员,将其值分配在栈中。这样做的原因是因为值类型的值其占用固定内存的大小。
C运算符sizeof来获得值类型所占byte大小。而方法中申明的引用类型变量,其在托管堆中存放着对象实例(对象实例至少会包含上述两个固定成员以及实例数据,可能),在栈中存放着指向该实例的地址。
当我new一个引用对象的时候,会先分配同步块索引(也叫对象头字节),然后是类型指针,最后是类型实例数据(静态字段的指针存在于方法表中)。会先分配对象的字段成员,然后分配对象父类的字段成员,接着再执行父类的构造函数,最后才是本对象的构造函数。这个多态的过程,对于CLR来说就是一系列指令的集合,所以不能纠结new一个子类对象是否会也会new一个父类对象这样的问题。而也正是因为引用类型的这样一个特征,我们虽然可以估计一个实例大概占用多少内存,但对于具体占用的大小,我们需要专门的工具来测量。
对于引用类型,u2=u1,我们在赋值的时候,实际上赋的是地址,那么我改动数据实际上是改动该地址指向的数据,这样一来,因为u2和u1都指向同一块区域,所以我对u1的改动会影响到u2,对u2的改动会影响到u1。如果我想互不影响,那么我可以继承IClone接口来实现内存克隆,已有的CLR实现是浅克隆方法,但也只能克隆值类型和String(string是个特殊的引用类型,对于string的更改,其会产生一个新实例对象),如果对包含其它引用类型的这部分,我们可以自己通过其它手段实现深克隆,如序列化、反射等方式来完成。而如果引用类型中包含有值类型字段,那么该字段仍然分配在堆上。
对于值类型,a=b,我们在赋值的时候,实际上是新建了个值,那么我改动a的值那就只会改动a的值,改动b的值就只会改动b的值。而如果值类型(如struct)中包含的有引用类型,那么仍是同样的规则,引用类型的那部分实例在托管堆中,地址在栈上。
我如果将值类型放到引用类型中(如:objecta=3),会在栈中生成一个地址,在堆中生成该值类型的值对象,还会再生成这类型指针和同步块索引两个字段,这也就是常说装箱,反过来就是拆箱。每一次的这样的操作,都会涉及到内存的分布、拷贝,可见,装箱和拆箱是有性能损耗,因此应该减少值类型和引用类型之间转换的次数。
但对于引用类型间的子类父类的转换,仅是指令的执行消耗,几尽没有开销。
选class还是struct
那么我到底是该new一个class呢还是选择struct呢?
通过上文知道对于class,用完之后对象仍然存在托管堆,占用内存。对于struct,用完之后直接由操作系统销毁。那么在实际开发中定义类型时,选择class还是struct就需要注意了,要综合应用场景来辨别。struct存在于栈上,栈和托管堆比较,最大的优势就是即用即毁。所以如果我们单纯的传递一个类型,那么选择struct比较合适。但须注意线程堆栈有容量限制,不可多存放超大量的值类型对象,并且因为是值类型直接传递副本,所以struct作为方法参数是线程安全的,但同样要避免装箱的操作。而相比较class,如果类型中还需要多一些封装继承多态的行为,那么class当然是更好的选择。
GC管理器
值得注意的是,当我new完一个对象不再使用的时候,这个对象在堆中所占用的内存如何处理?
在非托管世界中,可以通过代码手动进行释放,但在.NET中,堆完全由CLR托管,也就是说GC堆是如何具体来释放的呢?
当GC堆需要进行清理的时候,GC收集器就会通过一定的算法来清理堆中的对象,并且版本不同算法也不同。最主要的则为Mark-Compact标记-压缩算法。
这个算法的大概含义就是,通过一个图的数据结构来收集对象的根,这个根就是引用地址,可以理解为指向托管堆的这根关系线。当触发这个算法时,会检查图中的每个根是否可达,如果可达就对其标记,然后在堆上找到剩余没有标记(也就是不可达)的对象进行删除,这样,那些不在使用的堆中对象就删除了。
前面说了,因为nextObjPtr的缘故,在堆中分配的对象都是连续分配的,因为未被标记而被删除,那么经过删除后的堆就会显得支零破碎,那么为了避免空间碎片化,所以需要一个操作来让堆中的对象再变得紧凑、连续,而这样一个操作就叫做:Compact压缩。
而对堆中的分散的对象进行挪动后,还会修改这些被挪动对象的指向地址,从而得以正确的访问,最后重新更新一下nextObjPtr指针,周而复始。
而为了优化内存结构,减少在图中搜索的成本,GC机制又为每个托管堆对象定义了一个属性,将每个对象分成了3个等级,这个属性就叫做:代,0代、1代、2代。
每当new一个对象的时候,该对象都会被定义为第0代,当GC开始回收的时候,先从0代回收,在这一次回收动作之后,0代中没有被回收的对象则会被定义成第1代。当回收第1代的时候,第1代中没有被清理掉的对象就会被定义到第2代。
CLR初始化时会为0/1/2这三代选择一个预算的容量。0代通常以256KB-4MB之间的预算开始,1代的典型起始预算为512KB-4MB,2代不受限制,最大可扩展至操作系统进程的整个内存空间。
比如第0代为256K,第1代为2MB。我们不停的new对象,直到这些对象达到256k的时候,GC会进行一次垃圾回收,假设这次回收中回收了156k的不可达对象,剩余100k的对象没有被回收,那么这100k的对象就被定义为第1代。现在就变成了第0代里面什么都没有,第1代里放的有100k的对象。这样周而复始,GC清除的永远都只有第0代对象,除非当第一代中的对象累积达到了定义的2MB的时候,才会连同清理第1代,然后第1代中活着的部分再升级成第二代…
第二代的容量是没有限制,但是它有动态的阈值(因为等到整个内存空间已满以执行垃圾回收是没有意义的),当达到第二代的阈值后会触发一次0/1/2代完整的垃圾收集。
也就是说,代数越长说明这个对象经历了回收的次数也就越多,那么也就意味着该对象是不容易被清除的。
这种分代的思想来将对象分割成新老对象,进而配对不同的清除条件,这种巧妙的思想避免了直接清理整个堆的尴尬。
弱引用、弱事件
GC收集器会在第0代饱和时开始回收托管堆对象,对于那些已经申明或绑定的不经访问的对象或事件,因为不经常访问而且还占内存(有点懒加载的意思),所以即时对象可达,但我想在GC回收的时候仍然对其回收,当需要用到的时候再创建,这种情况该怎么办?
那么这其中就引入了两个概念:
WeakReference弱引用、WeakEventManager弱事件
对于这2两个不区分语言的共同概念,大家可自行扩展百度,此处就不再举例。
GC堆回收
那么除了通过new对象而达到代的阈(临界)值时,还有什么能够导致垃圾堆进行垃圾回收呢?还可能windows报告内存不足、CLR卸载AppDomain、CLR关闭等其它特殊情况。
或者,我们还可以自己通过代码调用。
.NET有GC来帮助开发人员管理内存,并且版本也在不断迭代。GC帮我们托管内存,但仍然提供了System.GC类让开发人员能够轻微的协助管理。这其中有一个可以清理内存的方法(并没有提供清理某个对象的方法):GC.Collect方法,可以对所有或指定代进行即时垃圾回收(如果想调试,需在release模式下才有效果)。这个方法尽量别用,因为它会扰乱代与代间的秩序,从而让低代的垃圾对象跑到生命周期长的高代中。
GC还提供了,判断当前对象所处代数、判断指定代数经历了多少次垃圾回收、获取已在托管堆中分配的字节数这样的三个方法,我们可以从这3个方法简单的了解托管堆的情况。
托管世界的内存不需要我们打理,我们无法从代码中得知具体的托管对象的大小,你如果想追求对内存最细微的控制,显然Cprincipal-objects
代码访问安全性
在.NETFramework中还有一个安全策略,叫做代码访问安全CodeAccessSecurity,也就是CAS了。
代码访问安全性在.NETFramework中是用来帮助限制代码对受保护资源和操作的访问权限。
举个例子,我通过创建一个FileIOPermission对象来限制对后续代码对D盘的文件和目录的访问,如果后续代码对D盘进行资源操作则报错。
FileIOPermission是代码控制访问文件和文件夹的能力。除了FileIOPermission外,还有如PrintingPermission代码控制访问打印机的权限、RegistryPermission代码控制操作注册表的权限、SocketPermission控制接受连接或启动Socket连接的权限。
对于这些通过代码来对受保护资源和操作的权限限制,也就是这些类名后缀为Permission的类,它们叫做Permissions(权限),都继承自CodeAccessPermission,都有如Demand,Assert,Deny,PermitOnly,IsSubsetOf,Intersect和Union这些方法,在MSDN上有完整的权限列表:https://msdn.microsoft.com/en-us/library/h846e9b3(v=vs.100).aspx
为了确定代码是否有权访问某一资源或执行某一操作,CLR的安全系统将审核调用堆栈,以将每个调用方获得的权限与要求的权限进行比较。如果调用堆栈中的任何调用方不具备要求的权限,则会引发安全性异常并拒绝访问。
出自https://docs.microsoft.com/zh-cn/dotnet/framework/misc/code-access-security
而除了Permissions权限,代码访问安全性机制还有权限集、证据、代码组、策略等概念。这些概念让CAS如此强大,但相应的,它们也让CAS变得复杂,必须为每个特定机器定义正确的PermissionSet和CodeGroups才能设置成一个成功的CAS策略。
考虑到这层原因,Microsoft.NET安全小组决定从头开始重建代码访问安全性。在.NETFramework4.0之后,就不再使用之前的那套CAS模型了,而是使用.NETFramework2.0中引入的安全透明模型,然后稍加修改,修改后的安全透明模型成为保护资源的标准方法,被称之为:安全透明度级别2
安全透明度2介绍:https://msdn.microsoft.com/en-us/library/dd233102(v=vs.100).aspx
.NETFramework4.0的安全更改:https://msdn.microsoft.com/en-us/library/dd233103(v=vs.100).aspx
一个完整的CAS演示:https://www.codeproject.com/Articles/5724/Understanding-NET-Code-Access-Security
对于安全透明度级别2我将不再介绍,感兴趣的可以看我推荐的这2篇文章,对Level2的安全透明度介绍的比较详细,包括实践、迁移。
https://www.red-gate.com/simple-talk/dotnet/.net-framework/whats-new-in-code-access-security-in-.net-framework-4.0—part-i/
https://www.red-gate.com/simple-talk/dotnet/net-framework/whats-new-in-code-access-security-in-net-framework-4-0-part-2/
———————————————-
须注意:
.NET平台上的安全机制,仅仅是.NET平台上的,因此它只限制于托管代码,我们可以直接调用非托管代码或进程通信间接调用非托管代码等多个手段来突破对托管代码操作资源的限制。
事实上,我们在平常项目中代码编写的安全机制(业务逻辑身份验证、项目框架验证)与这些平台级的安全机制没什么不同。我们可以理解为代码写的位置不一样,.NET安全机制是写在CLR组件中,而我们的安全机制是写在上层的代码中。这些平台级的标识更多的是和操作系统用户有关,而我们项目代码中的标识则是和在数据库中注册的用户有关,大家都是通过ifelse来去判断,判断的主体和格局不一样,逻辑本质都是相同的。
NETCore不支持代码访问安全性和安全性透明性。
.NET是什么
我在前文对.NET系统概述时,有的直接称.NET,有的称.NETFramework。那么准确来说什么是.NET?什么又是.NETFramework呢?
.NET是一个微软搭造的开发者平台,它主要包括:
1.支持(面向)该平台的编程语言(如C、IronPython、IronRuby…),2.用于该平台下开发人员的技术框架体系(.NETFramework、.NETCore、Mono、UWP等),1.定义了通用类型系统,庞大的CTS体系2.用于支撑.NET下的语言运行时的环境:CLR3..NET体系技术的框架库FCL3.用于支持开发人员开发的软件工具(即SDK,如VS2017、VSCode等)
.NETFramework是什么
事实上,像我上面讲的那些诸如程序集、GC、AppDomain这样的为CLR的一些概念组成,实质上指的是.NETFrameworkCLR。
.NET平台是微软为了占据开发市场而成立的,不是无利益驱动的纯技术平台的那种东西。基于该平台下的技术框架也因为商业间的利益从而和微软自身的Windows操作系统所绑定。所以虽然平台雄心和口号很大,但很多框架类库技术都是以Windows系统为蓝本,这样就导致,虽然.NET各方面都挺好,但是用.NET就必须用微软的东西,直接形成了技术-商业的绑定。
.NETFramework就是.NET技术框架组成在Windows系统下的具体的实现,和Windows系统高度耦合,上文介绍的.NET系统,就是指.NETFramework。
部署.netFramework:https://docs.microsoft.com/zh-cn/dotnet/framework/deployment/deployment-guide-for-developers
.NETFramework高级开发:https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/29eafad8(v%3dvs.90)
.NETFramework源码在线浏览:https://referencesource.microsoft.com/
如何在VS中调试.NETFramework源代码
最为关键的是pdb符号文件,没得符号就调不了,对于符号我们从微软的符号服务器上下载(默认就已配置),还得有源代码来调试。
点击工具-选项-调试-常规,如果你之前没有在该配置栏配置过,那么你就勾选启用源服务器支持、启用.netFramework源代码单步执行,然后将要求源文件与原始版本完全匹配给取消掉。
然后就是下载pdb符号文件了,如果想直接下载那么可以在调试-符号这栏将Microsoft符号服务器给勾上。如果想按需下载,那么在调试的时候,可以点击调试-窗口选择模块/调用堆栈来选择自己想加载的去加载。
然后至https://referencesource.microsoft.com/网站点击右上角下载源代码。当你调试代码的时候,会提示你无可用源,这个时候你再将你下载下来的源码文件给浏览查找一下就可以了。
如何配置VS来调试.NETFramework源码:https://referencesource.microsoft.com/net-implementation-support
.NETStandard开源代码:https://github.com/dotnet/standard
.NET官方开源项目链接
现在我将给出.NET相关的开源项目地址:
参与.NET和.NET开源项目的起点:https://github.com/Microsoft/dotnet
.NETCore:https://github.com/dotnet/core.NETCore文档:https://github.com/dotnet/docsASP.NETCore:https://github.com/aspnet/homeASP.NETCore文档:https://github.com/aspnet/DocsEntityFrameworkCore框架:https://github.com/aspnet/EntityFrameworkCoreASP.NETCoreMVC框架:https://github.com/aspnet/MvcEntityFramework6:https://github.com/aspnet/EntityFramework6.NETFramework源码:https://github.com/microsoft/referencesource.NETCore基类库:https://github.com/dotnet/corefx.NETCoreCLR:https://github.com/dotnet/coreclrRoslyn编译器:https://github.com/dotnet/roslynMVC5、WebAPI2、WebPages3框架源码:https://github.com/aspnet/AspNetWebStack.NETStandard:https://github.com/dotnet/standardKestrelHttpServer用于ASP.NETCore的跨平台Web服务器:https://github.com/aspnet/KestrelHttpServerVisualStudioCode源码:https://github.com/Microsoft/vscode一些优秀的.NET库、工具、框架、软件开源集合:https://github.com/quozd/awesome-dotnet一些常用框架对ASP.NETCore和.NETCore的支持报告:https://github.com/jpsingleton/ANCLAFS一些.NET下用于支持开发的开源项目集合:https://github.com/Microsoft/dotnet/blob/master/dotnet-developer-projects.md微软出品的分布式框架orleans:https://github.com/dotnet/orleansML.NET用于.NET的开源和跨平台机器学习框架:https://github.com/dotnet/machinelearning
VisualStudio
在文章最后,我还要简单的说下VisualStudio。
通过上文得知,只需要一个txt记事本+csc.exe我们就可以开发出一个.NET程序,那么与之相比,.NET提供的开发工具VS有什么不同呢?
我们用记事本+csc.exe来编写一个.NET程序只适合小打小闹,对于真正要开发一个项目而言,我们需要文件管理、版本管理、一个好的开发环境等。而vside则就是这样一个集成代码编辑、编译、调试、追踪、测试、部署、协作、插件扩展这样多个组件的集成开发环境,csc.exe的编译功能只是vside中的其中之一。使用vside开发可以节省大量的开发时间和成本。
sln解决方案
当你用VS来新建一个项目时,VS会先为你新建一个整体的解决方案。这个解决方案表现为.sln和.suo后缀格式的文件,它们均是文本文件,对解决方案右键属性可以进行相应的修改,也可以直接用记事本打开。
在sln中,定义了解决方案的版本及环境,如包含的项目,方案启动项,生成或部署的一些项目配置等,你可以通过修改或重新定义sln来更改你的整个解决方案。
而suo则包含于解决方案建立关联的选项,相当于快照,储存了用户界面的自定义配置、调试器断点、观察窗口设置等这样的东西,它是隐藏文件,可删除但建议不要删除。
我们可以通过对比各版本之间的sln来修改sln,也可以使用网上的一些转换工具,也可以直接点击VS的文件-新建-从现有代码创建项目来让项目在不同VS版本间切换。
VisualStudio2010-VisualStudio4.0
VisualStudio2013-VisualStudio14
VisualStudio2017-define指令,不用在每个文件头部定义,相当于csc中的/define)定义DEBUG/TRACE常量(用于调试输出的定义变量,如智能追踪的时候可以输出该变量)目标平台(指定当前面向什么处理器生成的程序集,相当于csc中的/platform。选择x86则生成的程序集生成32位程序,能在32/64位Intel处理器中使用。选择x64则生成64位,只能在64位系统中运行。选择AnyCPU则32位系统生成32位,64位系统则生成64位。注意:编译平台和目标调用平台必须保持一致,否则报错。生成的32位程序集不能调用64位程序集,64位也不能调用32位)、首选32位(如果目标平台是AnyCPU并且项目是应用程序类型,则生成的是32位程序集)允许不安全代码(unsafe开关,在c版本)、调试信息(相当于csc中的/debug。选择none则不生成任何调试信息,无法调试。选择full则允许将调试器附加到运行程序,生成pdb调试文件。选择pdb-only,自.NET2.0开始与full选项完全相同,生成相同的pdb调试文件。)、文件对齐(指定输出文件中节的大小)、DLL基址(起点地址)
3.在生成事件选项中可以设置生成前和生产后执行的命令行,我们可以执行一些命令。
4.在调试选项中有一栏叫:启用VisualStudio承载进程,通过在vshost.exe中加载运行项目程序集,这个选项可以增加程序的调试性能,启用后会自动在输出目录生成{程序集名称}.vshost.exe这样一个文件,只有当当前项目不是启动项目的时候才能删除该文件。
IntelliTrace智能追溯
还要介绍一点VS的是,其IntelliTrace智能追溯功能,该功能最早存在于VS2010旗舰版,是我用的最舒服的一个功能。
简单介绍,该功能是用来辅助调试的,在调试时可以让开发人员了解并追溯代码所产生的一些事件,并且能够进行回溯以查看应用程序中发生的情形,它是一个非常强大的调试追踪器,它可以捕捉由你代码产生的事件,如异常事件、函数调用(从入口)、ADO.NET的命令(Sql查询语句…)、ASP.NET相关事件、代码发送的HTTP请求、程序集加载卸载事件、文件访问打开关闭事件、Winform/Webform/WPF动作事件、线程事件、环境变量、Console/Trace等输出…
我们可以通过在调试状态下点击调试菜单-窗口-显示诊断工具,或者直接按Ctrl+Alt+F2来唤起该功能窗口。
当然,VS还有其它强大的功能,我建议大家依次点完菜单项中的调试、体系结构、分析这三个大菜单里面的所有项,你会发现VS真是一个强大的IDE。比较实用且方便的功能举几个例子:
比如从代码生成的序列图,该功能在vs2015之前的版本可以找到(https://msdn.microsoft.com/en-us/library/dd409377.aspx、https://www.zhihu.com/question/36413876)
比如模块关系的代码图,可以看到各模块间的关系
比如对解决方案的代码度量分析结果
比如调试状态下函数调用的代码图,我们可以看到MVC框架的函数管道模型
以及并行堆栈情况、加载的模块、线程的实际情况
还有如进程、内存、反汇编、寄存器等的功能,这里不再一一展示
链接
有关解决方案:https://msdn.microsoft.com/zh-cn/library/b142f8e7(v=vs.110).aspx
有关项目模板:https://msdn.microsoft.com/zh-cn/library/ms247121(v=vs.110).aspx
有关项目元素的说明介绍:https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2010/16satcwx(v%3dvs.100)
有关调试更多内容:https://docs.microsoft.com/zh-cn/visualstudio/debugger/
有关代码设计建议:https://docs.microsoft.com/zh-cn/visualstudio/code-quality/code-analysis-for-managed-code-warnings
有关IntelliTrace介绍:https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2010/dd264915(v%3dvs.100)
建议
我热爱编程。
我知道大多数人对技术的积累都是来自于平常工作中,工作中用到的就去学,用不到就不学,学一年的知识,然后用个五六年。
我也能理解人的理想和追求不同,有的人可能就想平淡点生活。有的人可能是过了拼劲,习惯了安逸。有的人已经认命了。
而我现在也每天饱满工作没多少时间,但在下班之余我仍然坚持每天都看一看书。
想学没时间学,想拼不知道往哪拼。有埋汰自己脑袋笨的,有说自己不感兴趣的。有明明踌躇满志,但总三天捕鱼两天晒网的。我身边的朋友大多都这样。
我想说,尽管我们每个人的境遇、思想、规划不同,但我肯定大家大部分是出于生计而工作。
而出于生计,那就是为了自己。而既然是为了自己,那就别每天浑浑噩噩过,即使你因各种原因而没有斗志。
编程来不得虚的,如果你没走上管理,那么你的技术好就是好,不好就是不好,混不得,一分技术一分钱。自己不扎实,你运气就不可能太好。
技术是相通的,操作系统、通信、数据结构、协议标准、技术规范、设计模式,语言只是门工具。要知其然也要知其所以然,只知道1个梨+1个梨=2个梨,不知道1个苹果+1个苹果等于啥就悲剧了。
那怎样提升自己?肯定不能像之前那样被动的去学习了。
光靠工作中的积累带来的提升是没有多少。你不能靠1年的技术重复3年的劳动,自己不想提升就不能怨天尤人。
上班大家都一样,我认为成功与否取决于你的业余时间。你每天下班无论再苦都要花一个小时来学习,学什么都行,肯定能改变你的人生轨迹。
比如你每天下班后都用一小时来学一个概念或技术点,那么300天就是300个概念或者技术点,这是何等的恐怖。
当然,这里的学要有点小方法小技巧的。不能太一条道摸到黑的那种,虽然这样最终也能成功,并且印象还深刻,但是总归效率是有点低的。
比如你从网上下载个项目源码,你项目结构不知道,该项目运用技术栈也不太了解,就一点一点的开始解读。这是个提升的好方法,但这样很累,可以成功,但是很慢。见的多懂的少,往往会因为一个概念上的缺失而在一个细小的问题上浪费很长时间。或者说一直漫无目的的看博客来了解技术,那样获取的知识也不系统。
我的建议是读书,书分两类,一类是讲底层概念的一类是讲上层技术实现的。
可以先从上层技术实现的书读起(如何连接数据库、如何写网页、如何写窗体这些)。在有一定编程经验后就从底层概念的书开始读,操作系统的、通信的、数据库的、.NET相关组成的这些…
读完之后再回过头读这些上层技术的书就会看的更明白更透彻,最后再琢磨git下来的项目就显得轻松了。
关于把网站源码分享导入win2008到此分享完毕,希望能帮助到您。
