官方解释
.NET Framework的核心是其运行库的执行环境,称为公共语言运行库(CLR)或.NET运行库。通常将在CLR的控制下运行的代码称为托管代码(managed code)。
运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为。
托管代码是可以使用20多种支持Microsoft .NET Framework的高级语言编写的代码,它们包括:C#, J#, Microsoft Visual Basic .NET, Microsoft JScript .NET, 以及C++。所有的语言共享统一的类库集合,并能被编码成为中间语言(IL)。运行库编译器(runtime-aware ompiler)在托管执行环境下编译中间语言(IL)使之成为本地可执行的代码,并使用数组边界和索引检查,异常处理,垃圾回收等手段确保类型的安全。
大白话解释
CLR能识别解析且编译的就是托管代码,其他的就是非托管代码
本节中,我们会涉及到托管和非托管的另一个区别:
在C/C++中,资源都是需要手动释放的,比如,你new了一个指针,用过之后就需要delete掉,否则就会造成内存泄露。
而在Java中,不必考虑资源释放的问题,Java的垃圾回收机制(GC,Garbage Collection)会保证失效的资源被自动释放。
而C#的机制与Java类似,运行于.net平台上的代码,分配的资源一般会自动由平台的垃圾回收器释放,这样的资源就是托管资源。
但是一些例外的资源,如System.IO.StreamReader等各种流、各种连接所分配的资源,需要显式调用Close()或Dispose()释放,这种资源就叫做非托管资源。
在C#的三大难点之前传:什么时候应该使用C#?中我提到过,C#的一大优势在于Windows平台下的界面编程。但由于C#并不是很普及,经常出现底层或后台代码采用C/C++编写的情况,此时,若选择C#作为界面语言,则必然遇到一个C#调用C++代码的问题。
比较普遍的解决方案就是,先将C/C++的代码生成为DLL动态运行库,再在C#中调用。
举个例子
在C中:
#include
#include
void DisplayHelloFromDLL()
{
printf ("Hello from DLL !\n");
}
void CallHelloFromDLL(char* cp)
{
printf (cp);
printf ("\n");
*cp='a';
cp++;
printf (cp);
printf ("\n");
}
在C#中:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestConsole
{
using System;
using System.Runtime.InteropServices; // DLL support
class Program
{
[DllImport(@"TestLib.dll")]
public static extern void DisplayHelloFromDLL();
[DllImport(@"TestLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void CallHelloFromDLL(StringBuilder s);
static void Main()
{
Console.WriteLine("This is C# program");
DisplayHelloFromDLL();
StringBuilder sb = new StringBuilder(100);
CallHelloFromDLL(sb);
Console.WriteLine(sb);
}
}
在混合编程中,涉及了几个要点。
;导出DLL函数
LIBRARY testLib
EXPORTS
DisplayHelloFromDLL
CallHelloFromDLL
DLL与C#之间如何进行数据传送?
这个问题其实很复杂,像int,double这种基本的数据类型,是很好传递的。到了byte和char,就有点复杂了,更复杂的还有string和stringBuilder,以及结构体的传递等。
若传递的是指针,有两种方法,一种是采用托管的方式,使用Intptr存储指针,并使用ref获得地址(&);另一种是在C#中编写非托管的代码,用unsafe声明:
unsafe
{
//非托管代码
}
在非托管代码中,即可进行指针相关的操作。
若传递的是函数指针,由于C#中没有函数指针的概念,因此采用委托(delegate)的方式。
若传递的是自定义结构体,也可以采用ref的方式传递。
这个如果有机会的话,我会单独整理一下。
extern “C”、CallingConvention =CallingConvention.Cdecl)等必要声明。
这里面也牵涉到复杂的语言机制,本文不再赘述。
既然CLR自动处理对象的垃圾回收,为什么还要去了解GC和内存分配呢?
答:当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对虚拟机的自动管理技术实施必要的监控和调节了。
.net CLR在运行我们的程序时,在内存中开辟了两块地方作不同的用处--托管栈和托管堆. 托管栈用来存放局部变量, 跟踪程序调用与返回. 托管堆用来存放引用类型. 引用类型总是存放于托管堆. 值类型通常是放在托管栈上面的. 如果一个值类型是一个引用类型的一部分,则此值类型随该引用类型存放于托管堆中.
C#的new操作符导致CLR执行以下步骤
①计算类型的字段(以及从基类型继承的字段)所需的字节数。
②加上对象的开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步快索引(32位:两个字段各需32位,所以每个对象要增加8字节。64位:每个字段各需64位,所以每个对象要增加16字节)(int=4字节;long=8字节)
③CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NextObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象的引用。就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,即下一个对象放入托管堆是的地址
垃圾收集器寻找不再使用的托管对象时, 其判断依据是当一个对象不再有引用指向它, 就说明此对象是可以释放了. 一些复杂的情况下可以出现一个对象指向第二个对象,第二个对象指向第三个对象,…就象一个链表. 那么, 垃圾收集器从哪里开始查找不再使用的托管对象呢? 以刚才所说的链表为例, 显然是应该从链表的开头开始查找.
那么,在链表开头的是些什么东东呢?
是局部变量, 全局变量, 静态变量, 指向托管堆的CPU寄存器. 在CLR中,它们被称之为根
如何判断这个对象该不该回收?
垃圾收集器首先假定所有在托管堆里面的对象都是不可到达的(或者说没有被引用的,不再需要的), 然后从根上的那些变量开始, 针对每一个根上的变量, 找出其引用的托管堆上的对象, 将找到的对象加入这个图, 然后再沿着这个对象往下找,看看它有没有引用另外一个对象, 有的话,继续将找到的对象加入图中,如果没有的话, 就说明这条链已经找到尾部了. 垃圾收集器就去从根上的另外一个变量开始找, 直到根上的所有变量都找过了, 然后垃圾收集器才停止查找. 值得一提的是, 在查找过程中, 垃圾收集器有些小的优化, 如: 由于对象间的引用关系可能是比较复杂的, 所以有可能找到一个对象, 而此对象已经加入图了, 那么垃圾收集器就不再在此条链上继续查找, 转去其他的链上继续找. 这样对垃圾收集器的性能有所改善.
垃圾收集器建好这个图之后, 剩下那些没有在这个图中的对象就是不再需要的. 垃圾收集器就可以回收它们占用的空间
CLR使用一种引用跟踪算法。引用跟踪算法只关心引用类型的变量,因为只有这种变量才能引用堆上的对象,我们将所有引用类型的变量都称为根。
①CLR开始GC时,首先暂停进程中的所有线程(这样可以防止线程在CLR检查期间访问对象并更改其状态)
②CLR进入GC标记阶段(这个阶段,CLR遍历堆中所有对象,将同步块索引字段中的一位设为0。这表明所有的对象都应该删除)
③CLR检查所有活动根(根为null,则CLR忽略这个根),查看他们引用了那些对象。如果引用了堆上的对象,CLR都会标记那个对象(将对象的同步块索引中的位设置为1)
④检查完毕后,堆中的对象要么标记。要么未标记。已标记的对象不能被垃圾回收,因为至少有一个根在引用它,我们说这些对象时可达的
⑤进入GC的压缩阶段,在这个阶段,CLR对堆中已标记的对象进行“乾坤大挪移”,压缩所有幸存下来的对象,使它们占用连续的内存对象
⑥压缩之后,根现在的引用还是原来的位置,而非移动之后的位置。所以作为压缩阶段的一部分,CLR还要从每个根减去所引用的对象在内存中的偏移的字节数。这样就能保证根还是引用和之前一样的对象;只是对象在内存中换了位置
①CLR初始化堆时为0代和1代选择预算容量(以kb为单位)。后期CLR会自动调节预算容量
②如果分配一个新的对象造成第0代超过预算,就必须启动一次垃圾回收
③经过垃圾回收之后,第0代的幸存者被提升到1代(第一代的大小增加);第0代又空了出来
④由于第0代已满,所以必须垃圾回收。但这一次垃圾回收器发现第1代用完了预算容量。所以这次垃圾回收器决定检查第1代和第0代的所有对象。两代被垃圾回收以后,第1代的幸存者提升到2代,第0代的幸存者提升到1代
2,垃圾回收触发的条件
①最常见触发条件:CLR在检查第0代超过预算时触发一次GC
②代码显示调用Sytem.GC的静态Collect方法
③Windows报告底内存情况
④CLR正在卸载AppDomain
⑤CLR正在关闭(CLR在进程正常终止时)
3,大对象
目前认为85000字节或更大的对象时大对象。(之前讨论的都是小对象)。大对象一般是大字符串(比如XML或JSON)或者用于I/O操作的字节数组(比如从文件或网络将字节读入缓冲区一遍处理)
①大对象不是在小对象的地址空间分配,而是在进程地址空间的其他地方分配
②目前版本的GC不压缩大对象,因为在内存中移动它们的代价过高
③大对象总是第2代,绝不可能是第0代或者第1代
文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib
文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang
文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些
文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器
文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距
文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器
文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn
文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios
文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql
文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...
文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120
文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数