C# 内存篇

C#程序在CLR上运行的时候,内存从逻辑上划分为两大块:堆(托管堆)和栈(堆栈)。

  • 堆:堆是一块动态分配的内存区域,用于存储对象和数据,堆内存的分配和释放由CLR(公共语音运行库)管理,通过垃圾回收(GC)机制自动清理不再使用的对象。

    • 灵活分配:堆内存可以动态分配和释放,适合存储大数据和复杂对象。
    • 自动回收:堆内存由垃圾回收器自动管理,无需程序员手动释放内存。
    • 访问速度较慢:由于堆内存是非连续分配的,访问速度相对较慢。
  • 栈:是一种后进先出的数据结构,主要用于存储局部变量、函数参数和返回地址等,栈的内存分配和释放由编译器自动管理,当函数调用结束时,栈顶的内存会自动释放。

    • 快速访问:由于栈内存是连续分配的,访问速度非常快。
    • 自动管理:栈内存由编译器自动分配和释放,无需程序员手动管理。
    • 有限空间:栈的空间相对较小,适合存储小数据。

将按照类型(值类型 vs 引用类型)和使用场景(局部变量、字段、数组元素等)来归纳总结:

1. 值类型(Value Types)

局部变量

  • 栈上分配 :当值类型作为方法中的局部变量时,通常是在栈上分配的。
void Method()
{int number = 42; // 局部变量,通常在栈上分配
}

方法参数

  • 栈上分配 :传递给方法的值类型参数是以值的形式复制到栈上的。
void Method(int param) // 参数param通常是栈上的
{//方法体
}

字段

  • 堆上分配 :如果值类型是类的实例字段,则它们会随着类实例一起存储在堆上。
public class MyClass
{public int Number; // 实例字段,位于堆上
}
  • 栈或堆上分配 :如果值类型是结构体(struct)的字段,其分配位置取决于该结构体本身的位置。如果结构体是局部变量或方法参数,则字段在栈上;如果是类的字段或集合的一部分,则在堆上。

数组元素

  • 堆上分配 :即使数组包含的是值类型,整个数组对象仍然会被分配到堆上。每个数组元素的行为就像类的实例字段一样,即它们也被存储在堆上。
int[] numbers = new int[10]; // 数组numbers在堆上,其元素也在堆上

闭包和异步方法

  • 堆上分配 :如果一个匿名函数捕获了局部变量,这些变量将被提升到堆上,以便它们可以在匿名函数的作用域之外保持有效。同样地,在异步方法中,局部变量可能会被编译器转换为状态机的一部分,并因此存储在堆上。
Func<int> GetIncrementer()
{int counter = 0;return () => ++counter; // counter将被提升到堆上
}

2. 引用类型(Reference Types)

局部变量

  • 栈上分配 :引用类型的局部变量本身存放在栈上,但它们指向的对象(如类实例)存放在堆上。
void Method()
{MyClass obj = new MyClass(); // obj是栈上的引用MyClass实例在堆上
}

方法参数

  • 栈上分配 :引用类型的参数也是栈上的引用,但它们指向的对象存放在堆上。
void Method(MyClass param) // param是栈上的引用,MyClass实例在堆上
{//方法体
}

字段

  • 堆上分配 :引用类型的字段总是存放在堆上,因为它们是类实例的一部分。
public class MyClass
{public string Name; // 字段Name在堆上
}

数组元素

  • 堆上分配 :引用类型数组的元素同样是堆上的引用,它们指向的对象也存放在堆上。
MyClass[] objects = new MyClass[10]; // 数组objects在堆上,其元素也在堆上

闭包和异步方法

  • 堆上分配 :与值类型相同,如果引用类型变量被捕获或用于异步方法中,它们也会被提升到堆上。

3. 静态成员

无论是值类型还是引用类型,静态成员总是存放在堆上的特殊区域,称为“静态字段区”或“类型加载器上下文”。这是因为静态成员属于类本身,而不是任何特定的实例。

public class MyClass
{public static int StaticInt; // 静态字段StaticInt在堆上的静态字段区public static string StaticString; // 静态字段StaticString在堆上的静态字段区
}

4. 常量(Constants)

常量在编译时就被解析并直接嵌入到使用它们的代码中,因此它们不会占用运行时的内存。

public const int MaxValue = 100; // 编译时常量,不占用运行时内存

5. 方法(Method)
方法是代码的一部分,不是数据,因此它们不属于值类型或引用类型。然而,方法可以是值类型或引用类型的成员,并且可以通过这些类型的实例或类型本身来调用。

方法的本质
  • 方法 :方法是包含可执行代码的逻辑单元,用于定义对象的行为。它们是类、结构体(struct)、接口等类型成员的一种。
  • 存储位置 :类中的方法(无论是实例方法还是静态方法)的代码部分并不会为每个实例或每次调用单独分配内存。相反,它们以一种共享的方式存储在内存中。具体来说,方法的代码和元数据存储在一个称为方法表(Method Table)的结构中,方法表是由CLR(Common Language Runtime)维护的,并且位于托管堆(managed heap)的一个特殊区域。
  • 调用 :当方法被调用时,一个新的栈帧(Stack Frame)会在调用栈(call stack)上创建。这个栈帧存在于线程的栈空间中。栈帧包含局部变量、方法参数、返回地址以及其他控制信息。对于实例方法,还包括一个指向当前实例的引用(this指针),对于值类型(如struct),当调用实例方法时,实际上是传递了一个副本,而不是直接操作原始值。
实例方法
  • 关联对象 :实例方法是与特定的对象实例关联的。这意味着它们可以通过对象的引用(对于引用类型)或直接通过值(对于值类型)来调用。
public class MyClass
{public void InstanceMethod() { }
}public struct MyStruct
{public void InstanceMethod() { }
}
  • this指针 :实例方法有一个隐式的this指针,它指向当前正在调用该方法的对象实例。对于值类型(如struct),当调用实例方法时,实际上是传递了一个副本,而不是直接操作原始值。
静态方法
  • 独立于对象 :静态方法不属于任何特定的对象实例,而是属于类型本身。因此,它们可以通过类型名直接调用,而不需要创建类型的实例。
public class MyClass
{public static void StaticMethod() { }
}// 调用静态方法
MyClass.StaticMethod();
  • this指针 :因为静态方法不属于任何实例,所以它们没有this指针,也不能访问非静态成员。

方法与委托

有时候,方法可以通过委托(delegates)来间接地被视为“类型”,但这并不是说方法本身成为了值类型或引用类型。委托实际上是一个引用类型,它可以引用一个或多个方法。

public delegate void MyDelegate();class Program
{static void Main(string[] args){MyDelegate del = new MyDelegate(SomeMethod);del(); // 调用SomeMethod}static void SomeMethod(){Console.WriteLine("Hello, World!");}
}

在这个例子中,MyDelegate 是一个引用类型,它可以引用 SomeMethod。但是请注意,这里引用的是方法的入口点,而不是方法本身成为了一种类型。

总结

  • 值类型 :
    • 局部变量和方法参数通常在栈上分配。
    • 类的实例字段、数组元素、闭包和异步方法中的变量则在堆上分配。
  • 引用类型 :
    • 局部变量和方法参数的引用在栈上分配,但它们指向的对象在堆上。
    • 字段和数组元素总是在堆上分配。
  • 静态成员 :
    • 无论是值类型还是引用类型,静态成员都在堆上的静态字段区分配。
  • 常量 :
    • 在编译时解析,不占用运行时内存。

示例

以下示例包含了结构体、方法、属性、字符串、数组、常量、委托以及引用类型对象的初始化,并且在方法内部嵌套这些元素,并给出详细的内存示意图(使用假设的地址来表示内存位置)。

using System;
using System.Collections.Generic;// 定义一个值类型的结构体
public struct StructExample
{public int Value { get; set; }public string Text { get; set; }// 内部方法public void PrintValue(){Console.WriteLine($"Struct Value: {Value}, Text: {Text}");}
}// 定义一个引用类型的类
public class ClassExample
{// 属性public string Name { get; set; }public int[] Numbers { get; set; }// 字符串字段private string _description;// 常量public const int MaxSize = 100;// 构造函数public ClassExample(string description){_description = description;}// 方法public void PrintDescription(){Console.WriteLine(_description);}// 嵌套方法,包含更多元素public void NestedMethod(){// 值类型的局部变量StructExample localStruct = new StructExample { Value = 42, Text = "Local Struct" };localStruct.PrintValue();// 引用类型的局部变量var nestedClass = new ClassExample("Nested Class") { Name = "Nested", Numbers = new int[] { 1, 2, 3 } };nestedClass.PrintDescription();Console.WriteLine($"Nested Class Name: {nestedClass.Name}");// 数组string[] stringArray = new string[] { "One", "Two", "Three" };// 委托Action<string> printString = (message) => Console.WriteLine(message);printString("Hello from delegate");// 修改引用类型的成员nestedClass.Numbers[0] = 100;Console.WriteLine($"Modified Nested Class Number: {nestedClass.Numbers[0]}");}
}// 定义一个委托
public delegate void MyDelegate(string message);class Program
{static void Main(string[] args){// 常量const int GlobalConst = 10;// 值类型的局部变量StructExample globalStruct = new StructExample { Value = 1, Text = "Global Struct" };globalStruct.PrintValue();// 引用类型的局部变量ClassExample globalClass = new ClassExample("Global Class") { Name = "Global", Numbers = new int[] { 5, 6, 7 } };globalClass.PrintDescription();Console.WriteLine($"Global Class Name: {globalClass.Name}");// 数组string[] globalStringArray = new string[] { "First", "Second", "Third" };// 调用方法globalClass.NestedMethod();// 修改引用类型的成员globalClass.Numbers[0] = 200;Console.WriteLine($"Modified Global Class Number: {globalClass.Numbers[0]}");// 使用委托MyDelegate myDelegate = (message) => Console.WriteLine($"MyDelegate: {message}");myDelegate("Invoking delegate in Main");}
}

内存示意图

栈 (Stack)

  • Program.Main 方法的栈帧
    • GlobalConst -> 10 (直接存储值)
    • globalStruct -> 0x1000 (指向栈上的结构体数据)
    • globalClass -> 0x2000 (指向堆上的 ClassExample 对象)
    • globalStringArray -> 0x3000 (指向堆上的字符串数组)
  • ClassExample.NestedMethod 方法的栈帧
    • localStruct -> 0x4000 (指向栈上的结构体数据)
    • nestedClass -> 0x5000 (指向堆上的 ClassExample 对象)
    • stringArray -> 0x6000 (指向堆上的字符串数组)
    • printString -> 0x7000 (指向堆上的委托实例)

堆 (Heap)

  • 0x2000 -> ClassExample 实例 (Name="Global Class", Numbers=[200, 6, 7])
  • 0x3000 -> 字符串数组 (["First", "Second", "Third"])
  • 0x5000 -> ClassExample 实例 (Name="Nested", Numbers=[100, 2, 3])
  • 0x6000 -> 字符串数组 (["One", "Two", "Three"])
  • 0x7000 -> 委托实例 (printString 指向的方法)

内存示意图解释

  • 值类型:如 globalStruct 和 localStruct,它们的数据直接存储在栈上。
  • 引用类型:如 globalClass 和 nestedClass,栈上只保存了指向堆中实际对象的引用(指针),实际的对象数据和数组等都存储在堆上。
  • 字符串:虽然字符串是引用类型,但在C#中它们被特殊处理,通常直接存储在堆上或作为字面量存储在程序集中。
  • 常量:如 GlobalConst,它是一个编译时常量,直接嵌入到IL代码中,不占用运行时栈或堆空间。
  • 委托:如 myDelegate 和 printString,它们是引用类型,存储在堆上,并且可以持有对方法的引用。

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

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

相关文章

springMVC---resultful风格

目录 一、创建项目 pom.xml 二、配置文件 1.web.xml 2.spring-mvc.xml 三、图解 四、controller 一、创建项目 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi…

RustDesk ID更新脚本

RustDesk ID更新脚本 此PowerShell脚本自动更新RustDesk ID和密码&#xff0c;并将信息安全地存储在Bitwarden中。 特点 使用以下选项更新RustDesk ID&#xff1a; 使用系统主机名生成一个随机的9位数输入自定义值 为RustDesk生成新的随机密码将RustDesk ID和密码安全地存储…

告别 Excel,拥抱 R 语言:开启数据分析新时代

在这个数据驱动的时代&#xff0c;数据分析已然成为每个行业的核心竞争力。从市场营销到金融领域&#xff0c;从医疗健康到教育行业&#xff0c;数据无处不在&#xff0c;深刻影响着每一个决策。然而&#xff0c;面对日益复杂的数据集&#xff0c;单纯依靠 Excel 进行分析&…

LabVIEW驱动电机实现样品自动搜索

利用LabVIEW控制电机驱动相机在XY平面上进行扫描&#xff0c;以检测样品位置。样品最初可能位于相机视野范围之外&#xff0c;需要实现自动搜索样品位置并完成精确定位扫描的功能。该系统需具有以下特点&#xff1a; 高效搜索&#xff1a;能够快速确定样品位置&#xff0c;缩短…

【C语言】_字符串拷贝函数strcpy

目录 1. 函数声明及功能 2. 使用示例 3. 注意事项 4. 模拟实现 4.1 第一版&#xff1a;基本功能判空const修饰 4.2 第二版&#xff1a;优化对于\0的单独拷贝 4.3 第三版&#xff1a;仿strcpy的char*返回值 1. 函数声明及功能 char * strcpy ( char * destination, cons…

大模型WebUI:Gradio全解11——Chatbots:融合大模型的多模态聊天机器人(3)

大模型WebUI&#xff1a;Gradio全解11——Chatbot&#xff1a;融合大模型的多模态聊天机器人&#xff08;3&#xff09; 前言本篇摘要11. Chatbot&#xff1a;融合大模型的多模态聊天机器人11.3 组件Chatbot及ChatMessage11.3.1 Chatbot&#xff1a;聊天机器人组件1. API参数2.…

细说STM32F407单片机窗口看门狗WWDG的原理及使用方法

目录 一、窗口看门狗的工作原理 1、递减计数器 2、窗口值和比较器 3、看门狗的启动 4、提前唤醒中断 二、窗口看门狗的HAL驱动程序 1、窗口看门狗初始化 2.窗口看门狗刷新 3.EWI中断及其处理 三、不开启EWI的WWDG示例 1、示例功能 2、项目设置 &#xff08;1&…

Docker部署Spring Boot + Vue项目

目录 前提条件 概述 下载代码 打开代码 Docker创建网络 MySQL容器准备 MySQL数据库配置 启动MySQL容器 测试连接MySQL 初始化MySQL数据 Redis容器准备 修改Redis配置 启动redis容器 部署后端 后端代码打包 上传jar包到Linux 创建Dockerfile 构建镜像 运行后…

基于Oracle与PyQt6的电子病历多模态大模型图形化查询系统编程构建

一、引言 1.1 研究背景阐述 在当今数字化时代,医疗行业正经历着深刻的变革,数字化转型的需求日益迫切。电子病历(EMR)作为医疗信息化的核心,其管理的高效性和数据利用的深度对于提升医疗服务质量、优化临床决策以及推动医学研究具有至关重要的意义。传统的电子病历管理系…

万字长文介绍ARINC 653,以及在综合模块化航空电子设备(IMA)中的作用

文章目录 一、引言二、ARINC 653背景三、整体系统架构四、应用/执行&#xff08;APEX&#xff09;接口五、ARINC 653 RTOS内部机制六、健康监测功能七、软件应用八、ARINC 653现状九、总结 一、引言 在现代航空领域&#xff0c;综合模块化航空电子设备&#xff08;IMA&#xf…

在eNSp上telnet一下吧

在上篇博客&#xff1a;DNS 我们提到了telnet和设备带外管理、带内管理&#xff0c;它确实是非常有趣的一个知识点哦&#xff0c;接下来我们一起来学习学习吧~ Telnet&#xff08;远程登陆协议&#xff09; Telnet基于TCP 23号端口&#xff0c;典型的C/S架构模式&#xff0c;是…

音频语言模型与多模态体系结构

音频语言模型与多模态体系结构 多模态模型正在创造语言、视觉和语音等以前独立的研究领域的协同效应。这些模型使用通用架构,将每种模式视为不同的“token”,使它们能够以一种与人类认知非常相似的方式联合建模和理解世界。 ​ ​可以将多模态分为两个主要领域:输入空间(…

【深度学习】关键技术-正则化(Regularization)

正则化&#xff08;Regularization&#xff09; 是一种用于防止模型过拟合的技术。它通过在损失函数中添加额外的约束项&#xff0c;限制模型的复杂度&#xff0c;从而提高模型的泛化能力。 正则化的主要作用 防止过拟合&#xff1a;通过抑制模型对训练数据的过度拟合&#xf…

怎么在iPhone手机上使用便签进行记录?

宝子们&#xff0c;在这个快节奏的时代&#xff0c;灵感的火花总是一闪而过&#xff0c;待办事项也常常让人应接不暇。好在咱们的 iPhone手机便签超给力&#xff0c;能满足各种记录需求&#xff01;今天就来给大家分享一下&#xff0c;如何在 iPhone 手机上巧用便签&#xff0c…

渗透测试之越权漏洞详解 水平越权 垂直越权 目录越权 SQL跨库查询越权 以及未授权漏洞 一篇文章说明白

目录 什么是越权 越权漏洞形成的原因是&#xff1a; 越权常见分类 漏洞产生条件 越权细分类 水平越权&#xff1a; 水平越权解释&#xff1a; 垂直越权&#xff1a; 垂直越权示例: 解释: 修复建议 例如 越权漏洞易发生的点 1.基础参数 2.多阶段验证 3.基于参数的访…

ANSYS Fluent学习笔记(七)求解器四部分

16.亚松弛因子 Controls面板里面设置&#xff0c;它能够稳定计算的过程。如果采用常规的迭代算法可能结果就会发生振荡的情况。采用亚松驰因子可以有助于残差的稳定。 他的取值范围是0-1&#xff0c;0代表没有亚松驰&#xff0c;1表示物理量变化很快&#xff0c;一般情况下取…

ComfyUI安装

项目地址&#xff1a;https://github.xyz/comfyanonymous/ComfyUI 下载地址(我下载时的最新版时v0.3.10)&#xff1a;https://github.xyz/comfyanonymous/ComfyUI/releases/tag/v0.3.10 加速下载地址&#xff1a;https://bgithub.xyz/comfyanonymous/ComfyUI/releases/tag/v0…

《C++11》静态断言(Static Assert)的使用与优势

C11引入了许多新特性&#xff0c;其中之一就是静态断言&#xff08;Static Assert&#xff09;。这是一种在编译时期进行断言的机制&#xff0c;它可以帮助我们在编译阶段就发现错误&#xff0c;而不是等到运行时才发现。这样可以大大提高代码的质量和稳定性。本文将详细介绍静…

cv::moments

1、 代表所有像素值之和。 2、 代表所有像素值乘以该位置处的x坐标&#xff0c;然后再求和。 3、 代表所有像素值乘以该位置处的y坐标&#xff0c;然后再求和。 4、 5、

JavaScript-正则表达式方法(RegExp)

RegExp 对象用于将文本与一个模式匹配。 有两种方法可以创建一个 RegExp 对象&#xff1a;一种是字面量&#xff0c;另一种是构造函数。 字面量由斜杠 (/) 包围而不是引号包围。 构造函数的字符串参数由引号而不是斜杠包围。 new RegExp(pattern[, flags])一.符集合 1.选择…