栈内存里面存放基本类型的变量和对象的引用变量
堆内存里面存放new创建的对象和数组
栈内存里面存放基本类型的变量和对象的引用变量
堆内存里面存放new创建的对象和数组
转载于:https://www.cnblogs.com/x10322/p/6024655.html
· 计算机执行程序时,组成程序的指令和程序所操作的数据都必须存放在某个地方
· 这个地方就是计算机的内存(也称为主存或随机访问存储器),类比人类的大脑,内存就是程序员的一切,非常重要
注意:主存和RAM的区别
8 bit(比特) = 1 byte (字节)
10M带宽:10M bps( bit per second )
内存分配方式
内存分配方式有三种:
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。
常见的内存错误及其对策如下:
编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。
内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。
有三种情况:
(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。(return ?)
(3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
【规则1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
【规则2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
【规则3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
【规则4】动态内存的申请与释放必须配对,防止内存泄漏。
【规则5】用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。
· 动态内存会被自动释放吗?
函数体内的局部变量在函数结束时自动消亡。很多人误以为示例是正确的。理由是p是局部的指针变量,它消亡的时候会让它所指的动态内存一起完蛋。这是错觉!
void Func(void)
{
char p = (char ) malloc(100);
// 动态内存会自动释放吗?
}
我们发现指针有一些“似是而非”的特征:
(1)指针消亡了,并不表示它所指的内存会被自动释放。
(2)内存被释放了,并不表示指针会消亡或者成了NULL指针。
这表明释放内存并不是一件可以草率对待的事。也许有人不服气,一定要找出可以草率行事的理由:
如果程序终止了运行,一切指针都会消亡,动态内存会被操作系统回收。既然如此,在程序临终前,就可以不必释放内存、不必将指针设置为NULL了。终于可以偷懒而不会发生错误了吧? 想得美。如果别人把那段程序取出来用到其它地方怎么办?
如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。
(1)判断指针是否为NULL,如果是则马上用return语句终止本函数。例如:
void Func(void)
{
A *a = new A;
if(a == NULL)
{
return;
}
…
}
(2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。例如:
void Func(void)
{
A *a = new A;
if(a == NULL)
{
cout << “Memory Exhausted” << endl;
exit(1);
}
…
}
(3)为new和malloc设置异常处理函数。例如Visual C++可以用_set_new_hander函数为new设置用户自己定义的异常处理函数,也可以让malloc享用与new相同的异常处理函数。
上述(1)(2)方式使用最普遍。如果一个函数内有多处需要申请动态内存,那么方式(1)就显得力不从心(释放内存很麻烦),应该用方式(2)来处理。 很多人不忍心用exit(1),问:“不编写出错处理程序,让操作系统自己解决行不行?”
不行。如果发生“内存耗尽”这样的事情,一般说来应用程序已经无药可救。如果不用exit(1) 把坏程序杀死,它可能会害死操作系统。道理如同:如果不把歹徒击毙,歹徒在老死之前会犯下更多的罪。
有一个很重要的现象要告诉大家。对于32位以上的应用程序而言,无论怎样使用malloc与new,几乎不可能导致“内存耗尽”。我在Windows 98下用Visual C++编写了测试程序,见示例。这个程序会无休止地运行下去,根本不会终止。因为32位操作系统支持“虚存”,内存用完了,自动用硬盘空间顶替。我只听到硬盘嘎吱嘎吱地响,Window 98已经累得对键盘、鼠标毫无反应。 我可以得出这么一个结论:对于32位以上的应用程序,“内存耗尽”错误处理程序毫无用处。这下可把Unix和Windows程序员们乐坏了:反正错误处理程序不起作用,我就不写了,省了很多麻烦。 必须强调:不加错误处理将导致程序的质量很差,千万不可因小失大。
void main(void)
{
float *p = NULL;
while(TRUE)
{
p = new float[1000000];
cout << “eat memory” << endl;
if(p==NULL)
exit(1);
}
}
上一篇文章简单整理了栈(stack), 堆(heap), 队列(queue)的结构
这一篇继续整理java中的 占内存,堆内存。
Java把内存划分成两种:一种是栈内存,一种是堆内存。
这里需要解释一下, 这里的堆内存是跟数据结构的堆是完全两码事。
一、栈内存
存放基本类型的变量,以及对象的引用值和函数主体,遵循先入后出的原则。
栈内存在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时:
Java在栈中为变量分配内存空间,当超过变量的作用在域后,
Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
Java中的代码是在函数体中执行的,每个函数主体都会被放在栈内存中,
举例子有一个main()函数。
main()函数里调用了save()函数,那么栈低存储的是main()函数其上面是save()函数。
等save()函数执行后,先销毁save()函数,再销毁main()函数。
栈的优势是,栈内存与堆内存相比是非常小的,存取速度比堆要快,栈数据可以共享。
但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
二、堆内存
给我的感觉是所有的引用类型的值是存在堆内存中,地址(指针)是存在栈内存中。
堆内存是区别于栈区、全局数据区和代码区的另一个内存区域。
堆内存在程序运行中,可以动态的调整申请大小。
堆内存的分配过程
当接收到程序的内存申请时:
1. 先游遍操作系统中的记录空闲内存地址的链表,寻找第一个复合大小的节点。
2. 分配给程序该节点的空间,内存空间中的首地址会记录本次分配的大小,为删除的时能正确的获取长度。
3. 把该节点从操作系统的空闲节点中删除。
找到的堆节点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。
堆内存的大小受限于计算机系统中有效的虚拟内存。所以堆内存获得的空间比较灵活,也比较大。
但是也是因为这些原因容易产生内存碎片,存取速度也比较慢。
三、内存回收
`栈内存`变量基本上用完就回收了。
`堆内存`中:
数组和对象等引用类型在没有变量指向它的时候才被视为垃圾。
在随后的一个`不确定的时间`被垃圾回收器释放掉。
因为这些原因回收内存之前会一直占用资源, 也不知道这些资源什么会回收释放。
四、其他数据存储
1、常量池:存放基本类型常量和字符串常量
2、静态域(static):存放静态成员(static定义的)编译的时候直接分配空间。要求程序代码中不允许有可变数据结构(比如可变数组)的存在.
3、非RAM存储:硬盘等永久存储空间
五、编译时分配的空间
最后我们再次已编译的角度去理解静态, 栈, 堆的存储方式。
1. 静态存储分配:
编译的时候直接分配空间, 所以会要求程序代码中不允许有可变数据结构(比如可变数组)的存在。
2. 栈存储分配:
是编译的时候时未知的, 但是运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存。
3. 堆存储分配:
是编译的时候时未知的, 运行时模块入口处都无法确定存储要求 数据结构的内存分配, 执行的过程也是可变的。所以存取速度也比较慢。
欢迎大家的意见和交流
email: li_mingxie@163.com
计算机里用于存放程序和数据的东西叫做外辅存储器,简称外存储其实,比如固态硬盘、机械硬盘、U盘、磁盘、光盘等等都属于外存储器。它们的作用就是用于存放大量的数据以及软件的,并且能够长期存放,而短期存放数据的则为内存。
计算机中用来存放程序和数据的部件是什么
该部件官方称呼是外存储器,也叫外辅存储器,它是计算机专门用于长时间存放数据、程序的设备。外存储器对于电脑不可或缺,也是我们在配电脑的时候比较看重的部位,它经历过不同的发展阶段,且产品的更迭速度较快。
计算机外存储器有哪些
1、硬盘
首先就是硬盘,它分为机械硬盘、固态硬盘。在之前大部分电脑呢都使用的是机械硬盘,它的结构简单、价格便宜、性能稳定。而现在大部分配电脑时会选择固态硬盘,因为它的速度更快,使用寿命更长,且价格也慢慢降下来了。
2、U盘
其次是我们用到的U盘,它里面也可以存储程序、数据等等,并且它还可以与不同的计算机接口连接,方便携带超级好用。除U盘以外,移动硬盘的性质与它类似,作用也比较雷同。
3、磁盘
在计算机刚刚进入中国市场的时候,当时磁盘还是非常流行的,它也能存放数据以及程序,但容量比较有限,且容易损坏,虽然价格很便宜,但使用寿命不长,很快就被市场所淘汰。
4、光盘
后来人们会选择将程序、数据刻录在光盘当中,于是光盘也成为外存储器的一种,现在很多电脑配件的驱动程序就是用光盘存放的,购买的时候可直接用光盘安装驱动程序,但它现在用得也非常少了,原因是不方便携带。