基础才是重中之重~理解内存中的栈和堆

news/2024/7/20 12:49:24 标签: c#, 内存管理

.NET中使用stack(栈)和heap(堆)两种结构在内存中存储数据,今天咱们就来说说这两个结构

  1. Value Types,值类型
          在C#中,值类型继承自System.ValueType的,它们分别是
          Bool,   byte ,  char, decimal, double, enu, float, int, long, sbyte, short, struct, uint, ulong, ushort
  2. Reference Types 引用类型
        
    引用类型包括所有的从System.Object继承下来的类型,它们分别是
           class, interface, delegate, object,string,其中string是一种特殊的引用类型,之后的文章中我会单独说它,.net中的gc(垃圾回收)主要是回收heap中的内存。
  3. 指针:在C#中它已经被遗忘,但不能不说,因为引用类型在内在中事实上是以指针的形式存储在Stack上的,而它的数据内容是在Heap上,也就是上引用类型其实是在stack上做了一个地址的引用。

Stack是自我维护的,意味着基本上不需要我们手动进行内存管理,它有自己的内存管理体系,对于window来说最高是1M,而.net最大只能用256K没有记错的话,如果超出这个范围就会出现溢出。 而heap则是我们建立object对象所存储的地方,它可以由我们自己用GC去回收也可以由.net自己去回收。

对于在教科书上的一个例子,我要说一下,就是斐波那切数列问题,一般书上都是用递归写的,这对于程序员的影响事实上是很大的,如果程序员用普通的递归写这个算法,那你的服务没准什么时候就挂了,原因是内存出现stack溢出的现象,具体的原因用“老赵”同志写了段话非常有代表性:它把普通递归改写成了尾递归解决了这个问题。

int FactorialTailRecursion(int n, int acc)
{
if (n == 0) return acc;
return FactorialTailRecursion(n - 1, acc * n); //变量仍然对程序有影响,所以编译器会一次一次堆累它使用的内存
}

改成尾递归之后:

int FactorialLoopOptimized(int n, int acc)
{
while (true)
{
if (n == 0) return acc;

acc *= n;
n--;
}
}

方法之前所积累下的各种状态对于递归调用结果已经没有任何意义,因此完全可以把本次方法中留在堆栈中的数据完全清除,把空间让给最后的递归调用。这样 的优化便使得递归不会在调用堆栈上产生堆积,意味着即时是“无限”递归也不会让堆栈溢出”。这其实才是尾递归的“正统”优化方式,那么我们先暂时忘记之前 的“循环优化”,从最简单的示例中查看这样的优化是如何进行的。

在这里,感谢老赵同志对基础的透彻讲解。

本文转自博客园张占岭(仓储大叔)的博客,原文链接:基础才是重中之重~理解内存中的栈和堆,如需转载请自行联系原博主。


http://www.niftyadmin.cn/n/684620.html

相关文章

第八篇 JVM的基本知识

jvm jvm运行和类加载全过程 先加载了类,加载完在进行初始化 初始化完成后加载该方法 jvm 先进行静态代码块 构造代码块 然后构造器 (多个静态资源取决于编译的先后顺序) ​ 类成员在对象创造前执行 static修饰的变量 在创建对象前进行初始化…

信号量 互斥量 读写锁 条件变量

互斥锁 [plain] view plaincopypthread_mutex_t mutexPTHREAD_MUTEX_INITIALIZER; 或 pthread_mutex_t mutex; int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) int pthread_mutex_lock(pthread_mutex_t *mutex) int pthread_mute…

第六篇2 多线程

多线程 推荐使用runnable对象 因为java是单继承多实现 单继承的局限性 实现多线程的方法:继承Thread类 实现runnable接口 实现callable接口 lamda表达式 函数式编程 ()->{} 死锁的四个条件:互斥 环路等待 不可剥夺 请求和保持 守护线程 线程分为用…

认识Java的第十一天(上)——接口

接口含有抽象方法含有默认方法和静态方法含有私有方法和私有静态方法基本实现抽象方法的使用默认方法的使用静态方法的使用私有的方法使用接口,是一种引用类型,是方法的集合,如果说类的内部封装了成员变量,成员方法和构造方法&…

程序猿的日常——Java中的集合列表

列表对于日常开发来说实在是太常见了,以至于很多开发者习惯性的用到数组,就来一个ArrayList,根本不做过多的思考。其实列表里面还是有很多玩法的,有时候玩不好,搞出来bug还得定位半天。所以这里就再啰嗦一下&#xff0…

Jsp学习(一)

JSP简介 1、定义 JSP(Java Server Pages) 是一种运行在 WEB 服务器的脚本语言,它的本质就是 Servlet当jsp页面第一次被访问时,服务器会把jsp编译成java文件,然后再把java编译成.class,然后创建该类对象,最后调用它的se…

感知机预测NBA总冠军

import numpy as np# 激活函数def sign(x): if x>0: return 1 else: return 0# 计算预测准确率函数def score(x,y): z0 for i in range(len(x)): if x[i]y[i]: z 1 print("准确率:",z/len(x)) # 训练…

认识Java的第十一天(中)——接口的多实现

接口的多实现接口的多实现抽象方法默认方法静态方法接口的多继承其他成员特点优先级的问题接口的多实现 在继承体系中,一个类只能继承一个父类,而对于接口来,那么一个类可以实现多个接口,这个就是我们的接口的多实现。 class 类名…