C# 通过不安全代码看内存加载

(注:本篇用点长,有点绕,耐心浏览)

C#中类型分为值类型和引用类型,值类型存储在堆栈中,是栈结构,先进后出,引用类型存储在托管堆中。接下来用不安全代码的地址,来看一下值类型和引用类型的存储。

项目文件

C#中使用不安全代码需要在项目文件中添加AllowUnsafeBlocks配置。

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net7.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><AllowUnsafeBlocks>true</AllowUnsafeBlocks></PropertyGroup>
</Project>

所有的测试案例都是定义两个特定类型的变量,然后查看它的内存地址,然后进行调用一个方法进行相加运算,然后分别在方法内输出变量和结查内存地址,最后返回主方法后变量的内存地址。

值类型:

static void TestDouble()
{var v1 = 1.00001d;var v2 = 2.00002d;Console.WriteLine("TestDouble v1 " + (long)&v1);Console.WriteLine("TestDouble v2 " + (long)&v2);Console.WriteLine("TestDouble v2-v1 " + ((long)&v2 - (long)&v1));var v3 = Add(v1, v2);Console.WriteLine("TestDouble v3 " + (long)&v3);Console.WriteLine("TestDouble v3-v2 " + ((long)&v3 - (long)&v2));Console.WriteLine("TestDouble v3-v1 " + ((long)&v3 - (long)&v1));
}static double Add(double v1, double v2)
{Console.WriteLine("Add v1 " + (long)&v1);Console.WriteLine("Add v2 " + (long)&v2);Console.WriteLine("Add v2-v1 " + ((long)&v2 - (long)&v1));var v3 = v1 + v2;Console.WriteLine("Add v3 " + (long)&v3);Console.WriteLine("Add v3-v2 " + ((long)&v3 - (long)&v2));Console.WriteLine("Add v3-v1 " + ((long)&v3 - (long)&v1));return v3;
}

25e8e1e5ff719370787338ea8573e084.png

v1的所在内存地址大于v2,最后运算完的v3是最小的,我们可以想象,v1放在栈的最后面,地址最大,然后放v2,最后放v3。回收时的顺序是反回来的。那么Add方法里,v2地址最大,但比TestDouble都要小,说明进栈要晚一些,接下来是v1进栈,最后是v3进栈,不过TestDouble里的每个变量都相差8,但方法里的就不是了,这是因为方法参数,返回值等信息,还要占一些内存空间。还有TestDouble的v3为什么能和v2相差8?不是有Add方法吗?原因是Add调用完后都出栈了,所以TestDouble的v3和v2是相邻的。

自定义结构体:

struct TestStruct
{public TestStruct(){i = 100;}public long i;
}
static void TestTestStruct()
{var v1 = new TestStruct();Console.WriteLine("TestStruct原v1对象地址= " + (long)&v1);var v2 = new TestStruct();Console.WriteLine("TestStruct原v2对象地址= " + (long)&v2);Console.WriteLine("TestStruct v2-v1 " + ((long)&v2 - (long)&v1));var v3 = Add(v1, v2);Console.WriteLine("TestStruct原v3对象地址= " + (long)&v3);Console.WriteLine("TestStruct v3-v2 " + ((long)&v3 - (long)&v2));
}
static TestStruct Add(TestStruct v1, TestStruct v2)
{Console.WriteLine("Add TestStruct v1对象地址= " + (long)&v1);Console.WriteLine("Add TestStruct v2对象地址= " + (long)&v2);Console.WriteLine("Add TestStruct  v2-v1 " + ((long)&v2 - (long)&v1));var v3 = new TestStruct();v3.i = v1.i + v2.i;Console.WriteLine("Add TestStruct v3对象地址" + (long)&v3);Console.WriteLine("Add TestStruct  v3-v2 " + ((long)&v3 - (long)&v2));return v3;
}

f2d3dce39de75449326f4c88ef4bb9cc.png

自定义struct与double类似,本质上double也是用struct定义的。

引用类型string

static void TestString()
{long ad1, ad2, ad3;var v1 = "aaaa";var v2 = "bbbb";fixed (char* p = v1){ad1 = (long)p;Console.WriteLine("TestString v1字符串地址= " + (long)p);}fixed (char* p = v2){ad2 = (long)p;Console.WriteLine("TestString v2字符串地址= " + (long)p);}Console.WriteLine("TestString v2-v1 " + (ad2 - ad1));var v3 = Add(v1, v2);fixed (char* p = v3){ad3 = (long)p;Console.WriteLine("TestString v3字符串地址= " + (long)p);}Console.WriteLine("TestString v3-v2 " + (ad3 - ad2));
}static string Add(string v1, string v2)
{long ad1, ad2, ad3;fixed (char* p = v1){ad1 = (long)p;Console.WriteLine("Add中v1字符串地址= " + (long)p);}fixed (char* p = v2){ad2 = (long)p;Console.WriteLine("Add中v2字符串地址= " + (long)p);}Console.WriteLine("Add中 v2-v1 " + (ad2 - ad1));var v3 = v1 + v2;fixed (char* p = v3){ad3 = (long)p;Console.WriteLine("Add中v3字符串地址= " + (long)p);}Console.WriteLine("Add中 v3-v2 " + (ad3 - ad2));Console.WriteLine("Add中 v3-v1 " + (ad3 - ad1));return v3;
}
static void TestString2()
{var v1 = "aaaa";var v2 = "bbbb";var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("TestString2 v1对象地址= " + (long)h1.AddrOfPinnedObject());var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("TestString2 v2对象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("TestString2 v2-v1 " + ((long)h2.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));var v3 = Add2(v1, v2);var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("TestString2 v3对象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("TestString2 v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));
}
static string Add2(string v1, string v2)
{var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("Add2中的v1对象地址= " + (long)h1.AddrOfPinnedObject());var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("Add2中的v2对象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("Add2 v2-v1 " + ((long)h2.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));var v3 = v1 + v2;var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("Add2中的v3对象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("Add2 v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));Console.WriteLine("Add2 v3-v1 " + ((long)h3.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));return v3;
}

c8263684c4f987330b8be1e849981fe1.png

字符串是引用类型,v1比v2内存地址小,进入Add后,v1和v2与传入的地址相同,因为是引用类型,Add方法里的v3接着往大走,并且与返回的v3是一个地址,这些没有问题。

string用了两种方法,发现两个方式v1都是aaaa,v2都是bbbb,因为字符串有留用性,所以两个方法的v1和v2是一样的;但两种方式调用了Add后,在Add里的v3都是aaaabbbb,都是拼接,但拼出来的字符串的地址不相同,所以这块没有留用。

自定class类型

class TestClass
{public int i = 100;
}
static void TestTestClass()
{var v1 = new TestClass();var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("TestTestClass v1对象地址= " + (long)h1.AddrOfPinnedObject());var v2 = new TestClass();var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("TestTestClass v2对象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("TestTestClass v2-v1 " + ((long)h2.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));var v3 = Add(v1, v2);var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("TestTestClass 3对象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("TestTestClass v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));
}
static TestClass Add(TestClass v1, TestClass v2)
{var h1 = GCHandle.Alloc(v1, GCHandleType.Pinned);Console.WriteLine("Add中的v1对象地址= " + (long)h1.AddrOfPinnedObject());var h2 = GCHandle.Alloc(v2, GCHandleType.Pinned);Console.WriteLine("Add中的v2对象地址= " + (long)h2.AddrOfPinnedObject());Console.WriteLine("Add中 v2-v1 " + ((long)h2.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));var v3 = new TestClass();v3.i = v1.i + v2.i;var h3 = GCHandle.Alloc(v3, GCHandleType.Pinned);Console.WriteLine("Add中的v3对象地址= " + (long)h3.AddrOfPinnedObject());Console.WriteLine("Add中 v3-v2 " + ((long)h3.AddrOfPinnedObject() - (long)h2.AddrOfPinnedObject()));Console.WriteLine("Add中 v3-v1 " + ((long)h3.AddrOfPinnedObject() - (long)h1.AddrOfPinnedObject()));return v3;
}

30e386e20410b49b5fbc61ae524e2167.png

自定义class,每次都是新地址,没有留用性,并且地址都是在增加。

当然引用类型的地址不是一成不变的,因为有垃圾回放,重新整理的过程,本例用用Pinned的方式固定,不过代码量少的情况也不一定能触发回收。

a09ef79218aa1d4e64c15ab5bad0c0e7.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]SDK与API区别

转载&#xff1a;https://www.zhihu.com/question/21691705/answer/149935191 SDK&#xff08;software development kit&#xff09;&#xff0c;中文可译为“软件开发工具包”。 一般都是一些被软件工程师用于为特定的软件包、软件架构、硬件平台、操作系统等建立应用软件的开…

详谈如何定制自己的博客园皮肤【转】

转自&#xff1a;http://www.cnblogs.com/jingmoxukong/p/7826982.html 目录 前言Quickstart定制博客园 CSS 的原理页面定制CSS代码博客侧边栏公告页首Html代码页脚Html代码定制细节独立控件小老鼠游戏动画动画时钟百度分享栏Github 角标签云背景动画动态标题文章内容样式定制带…

【ArcGIS微课1000例】0025:ArcGIS Online当前未连接到在线资源终极解决办法

ArcGIS Online在线资源列表: World Imagery: 底图服务: 中国地图彩色版: 打开ArcGIS时,系统托盘提示“ArcGIS Online当前未连接到在线资源”,如下图所示,如果无法连接到ArcGIS Online,则就无法添加在线资源,如World Imagery等。 关于该问题,网上有多种解决办法,然而…

华为笔记本Win11更新时由于驱动问题引起蓝牙鼠标经常断开问题解决方法

每次Win11升级后如果发现蓝牙鼠标经常断开&#xff0c;打开“华为电脑管家”执行以下操作即可&#xff1a; 然后更新蓝牙驱动 重启电脑OK。

【ArcGIS微课1000例】0026:ArcGIS10如何自定义工具条?

ArcGIS中,可以创建用户工具条,将自己常用的工具命令放到一起,方便实用提高工作效率。本文讲解如何自定义工具条并添加与删除工具。 1. 创建工具条 点击【自定义】菜单→【自定义模式】,如下图所示: 或者在任一工具条上点击最后面的下拉三角形→自定义: 以上两种方法都可…

基于 KubeSphere 流水线的 GitOps 最佳实践

背景Kubesphere 3.3.0 集成了 ArgoCD&#xff0c;但与笔者目前使用的 K8S 版本不兼容。再者&#xff0c;目前 Kubesphere 中持续集成和流水线打通还是不太友好&#xff0c;也缺少文档说明&#xff08;可能是笔者没有找到&#xff09;。目前遇到最主要的问题就是流水线制作完成的…

【ArcGIS微课1000例】0027:ArcGIS属性表(dbf)转Excel的4中方法

ArcGIS中的矢量数据Shapefile属性表存放在后缀名为.dbf的文件中,它是GIS数据分析的核心,如果将属性表转为别的平台使用,一般需要将其转为Excel格式。本文以ArcGIS自带矢量数据continent为例,讲解dbf转excel的常见4种方法。 文章目录 1. 表转Excel工具2. 直接打开3. 导出属性…

微信公众号自定义菜单直接跳转到小程序指定页面

首页我们要先拿到需要的小程序的页面地址&#xff1a;&#xff08;如何拿到小程序页面地址自行百度&#xff09; 然后登录公众号后台&#xff0c;添加自定义菜单&#xff1a; 菜单的路径选择“跳转小程序” &#xff0c;从绑定的小程序中选择要跳转的小程序&#xff0c;默认小程…

javascript高级程序设计 学习笔记 第五章 上

第五章引用类型的值(对象)是引用类型的一个实例。在 ECMAScript 中,引用类型是一种数据结构, 用于将数据和功能组织在一起。它也常被称为类,但这种称呼并不妥当。尽管 ECMAScript 从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构。引用…

HTTP协议之Expect爬坑

前言今天&#xff0c;在对接一个第三方平台开放接口时遇到一个很棘手的问题&#xff0c;根据接口文档组装好报文&#xff0c;使用HttpClient发起POST请求时一直超时&#xff0c;对方服务器一直不给任何响应。发起请求的代码如下&#xff1a;using (var httpClient new HttpCli…

【ArcGIS微课1000例】0028:ArcGIS根据属性快速分割生成多个shp文件

ArcGIS10.5及以上的版本提供了按属性分割工具,(分析工具->提取->按属性分割)工具。也可以使用10.2版本的分割工具,效果应该是一样的。本文演示使用分割工具批量快速提取一个县范围内的多个镇,生成多个镇矢量shp数据。 扩展阅读:【ArcGIS遇上Python】ArcGIS Python按…

Win11 恢复 Win10经典右键菜单 亲测有效

管理员运行命令&#xff1a; reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve 重…

【ArcGIS微课1000例】0029:ArcGIS绘制平行线(构造平行公路)

在实际工作中,有时需要绘制平行线,比如道路两边的边界线,可以使用“平行复制”功能快速绘制平行线,本文介绍如何使用“平行复制”功能快速绘制平行线。 1. 加载公路矢量 加载配套实验数据包中的数据0029.rar中的矢量数据:公路。 2. 生成平行公路 点击“编辑器”→“开始…

Win11 没有磁盘清理工具,如何清理磁盘旧的windows.old文件

win11 磁盘右键没有了“磁盘清理工具” 如果想清理文件&#xff0c;可以打开设置&#xff08;WinI&#xff09;系统—存储—清理建议 您也可以打开“存储感知”&#xff0c;系统会在需要时帮你自动清理文件。

.NET开发云原生应用,你只差给自己加个油

为什么要云原生&#xff08;Cloud Native&#xff09;Cloud表示应用程序位于云中&#xff0c;而不是传统的数据中心&#xff1b;Native表示应用程序从设计之初即考虑到云的环境&#xff0c;原生为云而设计&#xff0c;在云上以最佳姿势运行&#xff0c;充分利用和发挥云平台的弹…

java成员变量的初始化

2019独角兽企业重金招聘Python工程师标准>>> 类变量(static变量,不需要实例化对象也可以引用) 实例变量(非static变量,需要实例化对象) 局部变量(类的成员函数中的变量) 初始化方式: 构造函数初始化 变量声明时初始化 代码块初始化 java自动初始化(在构造函数执行之…

Win11 的日历 替代

Win11 的日历虽然漂亮&#xff0c;却少了很多小功能&#xff0c;特别是没有秒钟和日程的设计&#xff0c;让用惯了 Win10 的小伙伴大呼不满。原来曾经用过360带的日历&#xff0c;可惜不支持Win11&#xff0c;下面这个日历是一款功能强大、颜值很高的小工具&#xff0c;能够提供…

ReactNative--React简介

React 基础框架&#xff0c;是一些实现理念&#xff0c;不能用来做网页的开发和手机应用的开发 React.js 用来做网页开发 ReactNative 移动应用开发 在学习ReactNative之前&#xff0c;先学习React http://reactnative.cn/ 中文网 http://facebook.github.io/react-nativ…

【ArcGIS微课1000例】0030:ArcGIS利用MXD doctor工具分析并修复mxd地图文档

MXD Doctor 是一个独立的应用程序,位于 ArcGIS Desktop 安装目录下的 Tools 文件夹中。该工具可用于分析已损坏的 .mxd 文件。根据分析结果,可将已损坏的 .mxd 文件中所包含的实体复制到新的或现有 .mxd 文件中。 可以从所有程序 > ArcGIS > Desktop 工具中打开 MXD Do…

分析一个 .NET 写的 某 RFID标签系统 CPU暴涨

一&#xff1a;背景 1. 讲故事前段时间有位朋友说他的程序 CPU 出现了暴涨现象&#xff0c;由于程序是买来的&#xff0c;所以问题就比较棘手了&#xff0c;那既然找到我&#xff0c;就想办法帮朋友找出来吧&#xff0c;分析下来&#xff0c;问题比较经典&#xff0c;有必要和大…