.net托管与非托管以及GC(垃圾回收)_net 自定义的类都是非托管的吗-程序员宅基地

技术标签: 开发心得  .net  

 .net托管与非托管

官方解释

.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能识别解析且编译的就是托管代码,其他的就是非托管代码

托管资源与非托管资源的区别

  1. 编译运行过程不同
  2. 跨平台能力不同
  3. 程序性能不同

本节中,我们会涉及到托管和非托管的另一个区别:

  1. 释放资源的方式不同

在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);
    }
}

在混合编程中,涉及了几个要点。

  1. 如何在DLL中将函数接口暴露出来?
    有两种方式,一种是采用__declspec(dllexport)的声明,另一种是编写额外的def文件,如
    ;导出DLL函数
    LIBRARY testLib
    EXPORTS 
    DisplayHelloFromDLL
    CallHelloFromDLL
    
  2. DLL与C#之间如何进行数据传送?
    这个问题其实很复杂,像int,double这种基本的数据类型,是很好传递的。到了byte和char,就有点复杂了,更复杂的还有string和stringBuilder,以及结构体的传递等。
    若传递的是指针,有两种方法,一种是采用托管的方式,使用Intptr存储指针,并使用ref获得地址(&);另一种是在C#中编写非托管的代码,用unsafe声明:

    unsafe
    {
    //非托管代码
    }
    

    在非托管代码中,即可进行指针相关的操作。
    若传递的是函数指针,由于C#中没有函数指针的概念,因此采用委托(delegate)的方式。 
    若传递的是自定义结构体,也可以采用ref的方式传递。
    这个如果有机会的话,我会单独整理一下。

  3. extern “C”、CallingConvention =CallingConvention.Cdecl)等必要声明。
    这里面也牵涉到复杂的语言机制,本文不再赘述。

GC(垃圾回收)

既然CLR自动处理对象的垃圾回收,为什么还要去了解GC和内存分配呢?

答:当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对虚拟机的自动管理技术实施必要的监控和调节了。 

前言

.net CLR在运行我们的程序时,在内存中开辟了两块地方作不同的用处--托管栈和托管堆. 托管栈用来存放局部变量, 跟踪程序调用与返回. 托管堆用来存放引用类型. 引用类型总是存放于托管堆. 值类型通常是放在托管栈上面的. 如果一个值类型是一个引用类型的一部分,则此值类型随该引用类型存放于托管堆中.

C#的new操作符导致CLR执行以下步骤

①计算类型的字段(以及从基类型继承的字段)所需的字节数。

②加上对象的开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步快索引(32位:两个字段各需32位,所以每个对象要增加8字节。64位:每个字段各需64位,所以每个对象要增加16字节)(int=4字节;long=8字节)

③CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NextObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象的引用。就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,即下一个对象放入托管堆是的地址

根The root

垃圾收集器寻找不再使用的托管对象时, 其判断依据是当一个对象不再有引用指向它, 就说明此对象是可以释放了. 一些复杂的情况下可以出现一个对象指向第二个对象,第二个对象指向第三个对象,…就象一个链表. 那么, 垃圾收集器从哪里开始查找不再使用的托管对象呢? 以刚才所说的链表为例, 显然是应该从链表的开头开始查找.

那么,在链表开头的是些什么东东呢?

是局部变量, 全局变量, 静态变量, 指向托管堆的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代

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liuyongjie1992/article/details/122308450

智能推荐

计算机基础——操作系统-程序员宅基地

文章浏览阅读8.5k次,点赞28次,收藏38次。本章将会讲解计算机的操作系统。操作系统(Operating System,OS)就好比一个计算机内部的管理者,是管理和控制计算机硬件与软件资源的计算机程序,直接运行在“裸机”上的最基本的系统软件,任何其他应用软件都必须在操作系统的支持下才能运行,操作系统是用户和计算机的接口,同时也是计算机硬件和其他软件的接口。操作系统的功能包括管理计算机系统的硬件,软件及数据资源,控制程序运行,为其他应用软件提供支持等。_操作系统

Python之pip download 命令用法-下载指定平台和python版本的依赖包-程序员宅基地

文章浏览阅读1.9w次,点赞7次,收藏27次。pip download 和 pip install 有着相同的解析和下载过程,不同的是,pip install 会安装依赖项,而 pip download 会把所有已下载的依赖项保存到指定的目录 ( 默认是当前目录 ),此目录稍后可以作为值传递给 pip install --find-links 以便离线或锁定下载包安装_pip download

centos7设置密码策略_CentOS7 设置密码复杂度-程序员宅基地

文章浏览阅读3.4k次。在CentOS下设置密码复杂度分为两步(1)修改/etc/login.defs文件vim /etc/login.defsPASS_MAX_DAYS90   # 密码最长过期天数PASS_MIN_DAYS80    # 密码最小过期天数PASS_MIN_LEN10    # 密码最小长度PASS_WARN_AGE7    # 密码过期警告天数(2)..._echo 'mypassword' | openssl passwd -6 -stdin centos7

王斌老师的博客_王斌 github-程序员宅基地

文章浏览阅读480次。http://blog.sina.com.cn/s/blog_736d0b9101018cgc.html_王斌 github

ACM OJ Collection_htt//acm.wydtang.top/-程序员宅基地

文章浏览阅读737次。原文来自:http://blog.csdn.net/hncqp/article/details/4455263 ACM OJ Collection(排名不分先后):中国:浙江大学(ZJU):http://acm.zju.edu.cn/北京大学(PKU):htt_htt//acm.wydtang.top/

ios 自己服务器 苹果支付_修复苹果IOS支付-程序员宅基地

文章浏览阅读467次。更新记录1.0.0(2019-07-01)插件简介专门用来修复苹果IOS支付时出现"您已购买此App内购买项目。此项目将免费恢复"。问题描述首先在IOS平台里面创建“APP内购买项目”,选择的是“消耗型项目”,然后用uni-app官方的支付api进行支付,多支付几次,有时候就会出现提示“您已购买此App内购买项目。此项目将免费恢复”,特别是在沙盒测试里面支付很大几率出现,我明明选的是消耗型项目,应..._ios开发苹果支付恢复权益

随便推点

郁金香2021年游戏辅助技术中级班(七)_squad辅助科技-程序员宅基地

文章浏览阅读379次。郁金香2021年游戏辅助技术中级班(七)058-C,C++写代码HOOK分析封包数据格式A059-C,C++写代码HOOK分析封包数据格式B-detours劫持060-C,C++写代码HOOK分析封包数据格式C-过滤和格式化061-C,C++写代码HOOK分析封包数据格式D-写入配置文件062-C,C++写代码HOOK分析封包数据格式D-读取配置文件058-C,C++写代码HOOK分析封包数据格式A_squad辅助科技

ssh登录qemu虚拟机里的linux系统_qemu ssh连接-程序员宅基地

文章浏览阅读350次。上面的命令启动了一个带有NAT网络的QEMU虚拟机,并设置了端口转发,将主机的2222端口映射到虚拟机的22端口(SSH端口)。1、安装openssh,如果是根文件系统用buildroot构建,打开 BR2_PACKAGE_OPENSSH 开关。2、在qemu的启动脚本里增加。3、在虚拟机里增加一个新用户。4、向虚拟机里发送文件。_qemu ssh连接

用netty实现zcool_Netty框架入门-程序员宅基地

文章浏览阅读63次。一、概述Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。二、体系结构图三、Netty的核心结构Netty是典型的Reactor模型结构,在实现上,Netty中的Boss类充当mainReactor,NioWorker类充当subReactor(默认NioWorker的个数是当前服务器的..._channelconnected

SpringBoot 过滤器 filter 3种方法_spring boot filter 配置-程序员宅基地

文章浏览阅读4.7k次。最近Spring Boot项目做单点登录对接的时候,在配置过滤器的时候,找了几种方法,记录一下。欢迎评论补充沟通~由于之前JAVA Web项目最开始都有web.xml配置,随着框架慢慢的进化,从Spring Boot开始,已经没有了web.xml配置文件。那原来在web.xml里,配置的filter过滤器,在Spring Boot中怎么配置呢?注意,这个自定义类,也不能加@Component或@Configuration注解,加了就会初始化Filter了,过滤全部的路径了。_spring boot filter 配置

Ubuntu下,Python源码制作deb包_unmet build dependencies: debhelper (>= 11)-程序员宅基地

文章浏览阅读1k次。背景: 自己写了一个Python的qt客户端, 然后需要制作deb包1.先贴上代码的目录结构x@hostv6:~/mywork/my-client$ tree.├── 1├── debian│ ├── changelog│ ├── compat│ ├── control│ ├── copyright│ ├── README.Debian│ ├── rules│ ├── source│ │ ├── format│ │ └── loc..._unmet build dependencies: debhelper (>= 11)

13. PyQt5实现多页面切换之QTabWidget_pyqt5 tabwidget-程序员宅基地

文章浏览阅读2.8w次,点赞19次,收藏96次。QTabWidget 类,是一个实现多页面切换的类,该类已经实现了多页面切换的部分功能,只需再对其进行少量的设计(主要是要设计页面中的内容)便可实现多页面切换了。因此,使用该类实现多页面切换时,就不需要再使用 QStackedLayout 布局把页面与选项卡相关联,也不需要使用类似 QVBoxLayout 的布局把选项卡和页面放置在一起。与 QStackedLayout 布局原理相同,只有当前页面(即可见页面)是可见的,所有其他页面都不可见,用户可通过选择不同的选项卡来显示其对应的其他页面。_pyqt5 tabwidget

推荐文章

热门文章

相关标签