[No0000144]深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing)理解堆与栈1/4
前言
简介
这篇文章会包含堆与栈的基础知识,变量类型,变量工作原理。
在程序运行时,.NET FRAMEWORK把对象存储在内存中的两个位置:堆与栈,并且它们都会帮助我们更好的运行程序。堆与栈寄存在电脑的操作内存中,并包含我们需要的信息使整个程序运行正常。
堆与栈:有什么不同?
什么在堆和栈里
值类型:
- bool
- byte
- char
- decimal
- double
- enum
- float
- int
- long
- sbyte
- short
- struct
- uint
- ulong
- ushort
引用类型:
- class
- interface
- delegate
- object
- string
指针:
第三种被放于内存管理体制中的是类型的引用。这个引用通常被叫作指针。我们并不具体的使用指针,它们由CLR管理。一个指针(引用)是不同于引用类型的。我们定义它是一个引用类型,意味着我们可以通过指针访问它。一个指针占有一小块内存,这块内存指向另一块内存。指针占用在内存中的存储和其它的相同,只是存放的值既不是内存地址也不是空null。
指令:
总结
第一节到此结束,以后的章节里会介绍不同对象在堆和栈里存放的不同。
前言
简介
两个黄金规则
- 引用类型永远存储在堆里。
- 值类型和指针永远存储在它们声明时所在的堆或栈里。
栈工作原理
- public int AddFive(int pValue)
- {
- int result;
- result = pValue + 5;
- return result;
- }
下面是栈里发生的情况. 有必要提醒的是,我们现在假设当前代码产生的栈存储会放到所有既有项(栈里已经存储的数据)之上。一旦我们开始执行该方法,方法参数pValue会被放到栈上(以后的文章里会介绍参数传递)。
注意:方法并不存在栈里,图只是为了阐述原理而放的引用。
下一步,控制(线程执行方法)被传递到寄存在方法类型表里的AddFive()方法对应的指令集中。如果方法是第一次被触发,会执行JIT编译。
随着方法的执行,栈会分配一块内存给变量result存放。
方法执行完成,返回result。
该次任务在栈里所占的所有内存将被清理,仅一个指针被移动到AddFive()开始时所在的可用内存地址上。接着会执行栈里AddFive()下面一个方法(图里看不到)。
在这个例子当中,变量result被放到了栈里。事实上,方法体内每次定义的值类型变量都会被放到栈里。
当然值类型有时候也会被放到堆里,我们将会在下一节提到。
总结
前言
简介
值类型会存储在堆里?
代码图例
- public class MyInt
- {
- public int MyValue;
- }
里面包含一个值类型MyValue。
- public MyInt AddFive(int pValue)
- {
- MyInt result = new MyInt();
- result.MyValue = pValue + 5;
- return result;
- }
就像上一节介绍的一样,线程开始执行此方法,参数pValue将会被放到当前线程栈上。
堆栈原理对代码的影响
代码图例
- public int ReturnValue()
- {
- int x = new int();
- x = 3;
- int y = new int();
- y = x;
- y = 4;
- return x;
- }
我们会得到值 3。
- public class MyInt
- {
- public int MyValue;
- }
- public int ReturnValue2()
- {
- MyInt x = new MyInt();
- x.MyValue = 3;
- MyInt y = new MyInt();
- y = x;
- y.MyValue = 4;
- return x.MyValue;
- }
我们得到的值是4而不是3!(译外话:这是很简单,但相信还是有很多人不知道原理的)
- public int ReturnValue()
- {
- int x = 3;
- int y = x;
- y = 4;
- return x;
- }
- public int ReturnValue2()
- {
- MyInt x;
- x.MyValue = 3;
- MyInt y;
- y = x;
- y.MyValue = 4;
- return x.MyValue;
- }
总结
Even though with the .NET framework we don't have to actively worry about memory management and garbage collection (GC), we still have to keep memory management and GC in mind in order to optimize the performance of our applications. Also, having a basic understanding of how memory management works will help explain the behavior of the variables we work with in every program we write. In this article I'll cover the basics of the Stack and Heap, types of variables and why some variables work as they do.
There are two places the .NET framework stores items in memory as your code executes. If you are not yet familiar with them, let me introduce you to the Stack and the Heap. Both the Stack and Heap help us run our code. They reside in the operating memory on our machine and contain the pieces of information we need to make it all happen.
Stack vs. Heap: What's the difference?
The Stack is more or less responsible for keeping track of what's executing in our code (or what's been "called"). The Heap is more or less responsible for keeping track of our objects (our data, well... most of it; we'll get to that later).
Think of the Stack as a series of boxes stacked one on top of the next. We keep track of what's going on in our application by stacking another box on top every time we call a method (called a Frame). We can only use what's in the top box on the Stack. When we're done with the top box (the method is done executing) we throw it away and proceed to use the stuff in the previous box on the top of the Stack. The Heap is similar except that its purpose is to hold information (not keep track of execution most of the time) so anything in our Heap can be accessed at any time. With the Heap, there are no constraints as to what can be accessed like in the Stack. The Heap is like the heap of clean laundry on our bed that we have not taken the time to put away yet; we can grab what we need quickly. The Stack is like the Stack of shoe boxes in the closet where we have to take off the top one to get to the one underneath it.

The picture above, while not really a true representation of what's happening in memory, helps us distinguish a Stack from a Heap.
The Stack is self-maintaining, meaning that it basically takes care of its own memory management. When the top box is no longer used, it's thrown out. The Heap, on the other hand, must worry about Garbage collection (GC), which deals with how to keep the Heap clean (no one wants dirty laundry laying around, it stinks!).
What goes on the Stack and Heap?
We have four main types of things we'll be putting in the Stack and Heap as our code is executing: Value Types, Reference Types, Pointers, and Instructions.
Value Types:
In C#, all the "things" declared with the following list of type declarations are Value types (because they are from System.ValueType):
- bool
- byte
- char
- decimal
- double
- enum
- float
- int
- long
- sbyte
- short
- struct
- uint
- ulong
- ushort
Reference Types:
All the "things" declared with the types in this list are Reference types (and inherit from System.Object, except, of course, for object which is the System.Object object):
- class
- interface
- delegate
- object
- string
Pointers:
The third type of "thing" to be put in our memory management scheme is a Reference to a Type. A Reference is often referred to as a Pointer. We don't explicitly use Pointers, they are managed by the Common Language Runtime (CLR). A Pointer (or Reference) is different than a Reference Type in that when we say something is a Reference Type, it means we access it through a Pointer. A Pointer is a chunk of space in memory that points to another space in memory. A Pointer takes up space just like any other thing that we're putting in the Stack and Heap and its value is either a memory address or null.

Instructions:
You'll see how the "Instructions" work later in this article...
How is it decided what goes where? (Huh?)
Ok, one last thing and we'll get to the fun stuff.
Here are our two golden rules:
- A Reference Type always goes on the Heap; easy enough, right?
- Value Types and Pointers always go where they were declared. This is a little more complex and needs a bit more understanding of how the Stack works to figure out where "things" are declared.
The Stack, as we mentioned earlier, is responsible for keeping track of where each thread is during the execution of our code (or what's been called). You can think of it as a thread "state" and each thread has its own Stack. When our code makes a call to execute a method the thread starts executing the instructions that have been JIT compiled and live on the method table, it also puts the method's parameters on the thread Stack. Then, as we go through the code and run into variables within the method, they are placed on top of the Stack. This will be easiest to understand with an example.
Take the following method:
public int AddFive(int pValue)
{
int result;
result = pValue + 5;
return result;
}
Here's what happens at the very top of the Stack. Keep in mind that what we are looking at is placed on top of many other items already living in the Stack:
Once we start executing the method, the method's parameters are placed on the Stack (we'll talk more about passing parameters later).
NOTE : the method does not live on the stack and is illustrated just for reference.

Next, control (the thread executing the method) is passed to the instructions to the AddFive() method which lives in our type's method table, a JIT compilation is performed if this is the first time we are hitting the method.

As the method executes, we need some memory for the "result" variable and it is allocated on the Stack.

The method finishes execution and our result is returned.

And all memory allocated on the Stack is cleaned up by moving a pointer to the available memory address where AddFive() started and we go down to the previous method on the stack (not seen here).

In this example, our "result" variable is placed on the stack. As a matter of fact, every time a Value Type is declared within the body of a method, it will be placed on the stack.
Now, Value Types are also sometimes placed on the Heap. Remember the rule, Value Types always go where they were declared? Well, if a Value Type is declared outside of a method, but inside a Reference Type then it will be placed within the Reference Type on the Heap.
Here's another example.
If we have the following MyInt class (which is a Reference Type because it is a class):
public class MyInt
{
public int MyValue;
}
and the following method is executing:
public MyInt AddFive(int pValue)
{
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
Then just as before, the thread starts executing the method and its parameters are placed on sthe thread's stack.

Now is when it gets interesting.
Because MyInt is a Reference Type, it is placed on the Heap and referenced by a Pointer on the Stack.

After AddFive() is finished executing (like in the first example), and we are cleaning up...

we're left with an orphaned MyInt in the Heap (there is no longer anyone in the Stack standing around pointing to MyInt)!

This is where the Garbage Collection (GC) comes into play. Once our program reaches a certain memory threshold and we need more Heap space, our GC will kick off. The GC will stop all running threads (a FULL STOP), find all objects in the Heap that are not being accessed by the main program and delete them. The GC will then reorganize all the objects left in the Heap to make space and adjust all the Pointers to these objects in both the Stack and the Heap. As you can imagine, this can be quite expensive in terms of performance, so now you can see why it can be important to pay attention to what's in the Stack and Heap when trying to write high-performance code.
Ok, that's great, but how does it really affect me?
Good question.
When we are using Reference Types, we're dealing with Pointers to the type, not the thing itself. When we're using Value Types, we're using the thing itself. Clear as mud, right?
Again, this is best described by example.
If we execute the following method:
public int ReturnValue()
{
int x = new int();
x = 3;
int y = new int();
y = x;
y = 4;
return x;
}
We'll get the value 3. Simple enough, right?
However, if we are using the MyInt class from before:
public class MyInt
{
public int MyValue;
}
and we are executing the following method:
public int ReturnValue2()
{
MyInt x = new MyInt();
x.MyValue = 3;
MyInt y = new MyInt();
y = x;
y.MyValue = 4;
return x.MyValue;
}
What do we get? 4!
Why?... How does x.MyValue get to be 4? Take a look at what we're doing and see if it makes sense:
In the first example everything goes as planned:
public int ReturnValue()
{
int x = 3;
int y = x;
y = 4;
return x;
}

In the next example, we don't get "3" because both variables "x" and "y" point to the same object in the Heap.
public int ReturnValue2()
{
MyInt x;
x.MyValue = 3;
MyInt y;
y = x;
y.MyValue = 4;
return x.MyValue;
}

Hopefully this gives you a better understanding of a basic difference between Value Type and Reference Type variables in C# and a basic understanding of what a Pointer is and when it is used. In the next part of this series, we'll get further into memory management and specifically talk about method parameters.
For now...
Happy coding.
[No0000144]深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing)理解堆与栈1/4的更多相关文章
- [No0000145]深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing)理解堆与栈2/4
前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC).另外,了解内存管理可以帮助我们理解在每一个程 ...
- [No0000147]深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing)理解堆与栈4/4
前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC).另外,了解内存管理可以帮助我们理解在每一个程 ...
- [No0000146]深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing)理解堆与栈3/4
前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC).另外,了解内存管理可以帮助我们理解在每一个程 ...
- Java 堆内存与栈内存异同(Java Heap Memory vs Stack Memory Difference)
--reference Java Heap Memory vs Stack Memory Difference 在数据结构中,堆和栈可以说是两种最基础的数据结构,而Java中的栈内存空间和堆内存空间有 ...
- java - Stack栈和Heap堆的区别
首先分清楚Stack,Heap的中文翻译:Stack—栈,Heap—堆. 在中文里,Stack可以翻译为“堆栈”,所以我直接查找了计算机术语里面堆和栈开头的词语: 堆存储 ...
- 内存,堆,栈,heap,stack,data
1. 基本类型占一块内存. 引用类型占两块. 2. 类是静态概念. 函数中定义的基本类型变量和对象的引用类型变量都在函数的栈内存. 局部变量存在栈内存. new创建的对象和数组,存在堆内存. java ...
- iOS:堆(heap)和栈(stack)的理解
Objective-C的对象在内存中是以堆的方式分配空间的,并且堆内存是由你释放的,即release 栈由编译器管理自动释放的,在方法中(函数体)定义的变量通常是在栈内,因此如果你的变量要跨函数的话就 ...
- iOS中的堆(heap)和栈(stack)的理解
操作系统iOS 中应用程序使用的计算机内存不是统一分配空间,运行代码使用的空间在三个不同的内存区域,分成三个段:“text segment “,“stack segment ”,“heap segme ...
- stm32 堆和栈(stm32 Heap & Stack)【worldsing笔记】
关于堆和栈已经是程序员的一个月经话题,大部分有是基于os层来聊的. 那么,在赤裸裸的单片机下的堆和栈是什么样的分布呢?以下是网摘: 刚接手STM32时,你只编写一个 int main() ...
随机推荐
- 【Babble】批量学习与增量学习、稳定性与可塑性矛盾的乱想
一.开场白 做机器学习的对这几个词应该比较熟悉了. 最好是拿到全部数据,那就模型慢慢选,参数慢慢调,一轮一轮迭代,总能取得不错效果. 但是面对新来数据,怎么能利用已经训练好的模型,把新的信息加进去? ...
- SpringCloud服务间调用
SpringCloud服务间的调用有两种方式:RestTemplate和FeignClient.不管是什么方式,他都是通过REST接口调用服务的http接口,参数和结果默认都是通过jackson序列化 ...
- C++ 重载运算符和重载函数
C++ 重载运算符和重载函数 C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载. 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是 ...
- java 路径分隔符自动适配
linux文件路径分隔符为 / ,windows的文件路径分隔符为 \ ,在开发项目过程中不确定用户使用何种操作系统,就需要自动适配路径. 目前已知java提供两种方法获取文件路径分割符: F ...
- android sdk manager 代理设置
启动 Android SDK Manager ,打开主界面,依次选择「Tools」.「Options...」,弹出『Android SDK Manager - Settings』窗口: 在『Andro ...
- oracle 11g rac asm磁盘组增加硬盘
要增加磁盘的磁盘组为:DATA 要增加的磁盘为: /dev/sde1 在第一个节点上:[root@rac1 ~]# fdisk /dev/sdeDevice contains neither a va ...
- Java知多少(101)图像缓冲技术
当图像信息量较大,采用以上直接显示的方法,可能前面一部分显示后,显示后面一部分时,由于后面一部分还未从文件读出,使显示呈斑驳现象.为了提高显示效果,许多应用程序都采用图像缓冲技术,即先把图像完整装入内 ...
- SpringDataJPA - 复杂查询总结 (多表关联 以及 自定义分页 )
实体类 @Entity @Table(name = "t_hotel") @Data public class THotel { @Id private int id; priva ...
- Android实现电话录音功能
需求分析 电话录音是在通话的时候进行录音,所以需要使用一个服务来完成功能. 需要监听电话的状态,分为三种状态: 空闲状态 TelephonyManager.CALL_STATE_IDLE 响铃状态 ...
- scala中隐式转换之隐式类
/** * Created by root * Description :隐式类: * 1.其所带的构造参数有且只能有一个:并且构造器的参数是转换之前的对象 * 2.隐式类必须被定义在类,伴生对象和包 ...