本节是操作系统系列教程的第三篇文章,属于操作系统第一章即基础篇,在真正开始操作系统相关章节前在这一部分回顾一些重要的主题,以下是目录,由于本文篇幅较多因此会按上篇、中篇、下篇三次发布,目录中黑体为本篇内容。
什么是内存
c/c++内存模型
**堆区与栈区的本质
**
**java、python等内存模型
**
**java内存模型
**
**jave中的堆区与栈区是如何实现的
**
python内存模型
指针与引用
进程的内存模型
幻想大师-操作系统
总结
堆与栈的本质是什么在编程语言中,堆区和栈区本质上都是内存,因此二者在本质上没有任何区别,只不过这两块内存的使用方式是不一样的。
在数据结构与算法中,我们也有堆和栈的概念,但那里指的不是内存,而是两种数据结构。
你可能会想,我们为什么要费尽心力的提出堆和栈这两个概念呢?之所以需要区分两种内存用法,根源在于: 内存是有限的 。
如果计算机内存是无限的,那么我们根本就不用这么麻烦的给内存划分两个区域,在其中的一个区域中这样使用内存,另一区域那样使用内存,这些都是不需要的。即使在今天pc内存普遍都在8g、16g,这依然是不够的,因此我们需要合理的来安排内存的使用,堆和栈就是为达到这一目的而采用的技术。
你会发现栈其实是一种非常巧妙的内存使用方法。函数调用完成后,函数运行过程中占用的内存就会被释放掉,这样,只要程序员代码写的合理(栈帧不至于过大),那我们程序就可以一直运行下去,而不会出现内存不足的现象。程序员在栈区不需要担心内存分配释放问题,因为这一切都是自动进行的。而如果程序员想自己控制内存,那么可以选择在堆上进行内存分配。因此这里提供了两种选择,一种是“自动的”,一种是“手动的”,目的都是在合理使用内存的同时提供给程序员最大的灵活性。
堆和栈是计算机科学中很优秀的设计思想,这种设计思想充分的体现了计算机如何合理且灵活的使用有限资源。
堆区和栈区对c/c++程序员来说就是实实在在的内存,而对于java、python等语言的程序员来说又该如何理解内存呢?
java、python等内存模型当java、python等语言的程序在执行时其解释器的内存布局同样如下图所示,我们之前讲过,解释器也是一个c/c++程序,因此这里的代码段包含的是解释器的实现代码而不是java、python等代码,这一点大家一定要注意。
c/c++程序员面对的是实实在在的物理内存,java、python等程序面对的是解释器。
c/c++分配内存是直接在物理内存中进行的,而java、python等程序是将内存分配请求交给解释器,解释器再去物理内存上进行分配。希望大家务必理解这一点。
java、python等程序员是看不到如下图所示的内存布局的,因为这一切都是解释器才能看到的,解释器对java、python等程序员屏蔽了这些。java、python等程序员也无需关心解释器的内存布局。
java、python等程序的一大优点就是内存的自动化管理,而c/c++程序员需要自己来管理从堆上分配的内存。内存管理这一项工作在java、python等程序中被解释器接管了,解释器的这项功能被称为“垃圾回收器”。
在非c/c++语言中,我们来看两个有代表性的语言,首先我们看一下java。
java内存模型java的内存模型中同样有栈和堆这样的概念,如下图所示,在java函数中我们定义的内置数据类型比如int a = 0,是直接存放在栈上的,引用类型,也就是用new关键字定义的变量是分配在堆上的。和c/c++一样,每个java函数在执行时都有自己的栈帧。随着函数的调用,栈不断的扩大。当函数调用完毕后栈帧被回收,在堆上分配的变量依然可以被后续函数使用。java程序员无需像c/c++程序员一样需要关心内存回收的问题,这一切都是java的解释器jvm来管理的。
在用法上java中的堆和栈和c/c++是一样的,只不过java程序员无需关心内存的释放问题。但是好奇的同学可能会问,c/c++中的堆和栈我已经清楚了,因为c/c++程序运行时在内存中的样子已经在《c/c++内存模型》这一小节中详细的讲述了,那么java中的堆和栈在内存中是什么样子的呢,就是和上图一样吗?要回答这个问题,就要涉及到java中的堆和栈是如何实现的。
java中的堆和栈是如何实现的如果你自己设计过一门语言的话,你应该会很清楚这个问题。
我们先回答上一节中提到的问题,那就是java中的堆和栈就是如上图所示的那样吗?是这样的,作为java程序员在写代码时脑海里有上面这张图基本上就够用了。但是,java中的堆和栈不同于c/c++当中的堆和栈。
我们已经知道java中的内存管理其实是解释器jvm来搞定的,作为c/c++程序,jvm的内存布局就如下图所示。
一般情况下,当jvm运行一个java函数时需要在堆上创建出java函数的栈帧,然后把这些栈帧放入栈中(这里的栈指的是具有先进后出性质的数据结构)。希望大家不要被这句话绕晕,这里出现了两个“栈”,但是含义完全不同。
java栈帧:指的是上图中我们看到的栈。
栈帧放入到栈:我们在数据结构课程中都学过栈,栈有push和pop两种操作,把栈帧放入栈指的是把栈帧push到jvm所持有的栈这种数据结构当中,以此来模拟c/c++程序执行过程中函数栈帧先进后出的这种性质,当一个java函数被执行完毕后,jvm pop掉该函数的栈帧。
如果你想在代码级别来理解这个过程,大体上可以参考下面的代码,注意jvm是c/c++程序,这里的代码是一个极其简单的描述。你可以看到如何组织栈帧完全是jvm设计者来决定的,只要栈帧具备先进后出的性质就可以。
void runjavafunction(jvm* jvm, string javafunction) { // 在堆上申请一块空间,用于存放java栈帧 stackframe* frame = (stackframe*) malloc(sizeof(stackframe)); // 把要使用的栈帧push到jvm的函数调用栈中 jvm->stack->push(frame); // 在申请的栈帧上执行java函数 run(javafunction, frame); // 执行完毕后pop掉该函数栈帧 jvm->stack->pop();}jvm会在自己的堆中为用new修饰的对象创建内存,这里的堆就是如上图所示的堆,是可以要记住jvm是一个c/c++程序,jvm看到的堆才是如上图所示的那样。所以你会发现,一般情况下, java中的栈和java对象都是jvm在自己的堆上分配出来的 ,这就是java中堆和栈是如何实现的。
在讲解完java的内存模型后,我们来看一下python的。
python内存模型python的内存模型和java其实是类似的,java程序员脑海中的那张图同样适用于python程序员。
python语言中的解释器比较多,比如cpython,pypy等,在这里我们以python默认的解释器cpython为例来说明,我们已经知道了解释器其实也是一个c程序,cpython也不例外,下图左侧就是我们已经熟悉的c/c++内存布局,我们把堆区放大,如下图右侧所示。我们可以看到python的解释器把自己的堆区划分成了两部分,分别是object-specific memory区域,以及python core区域:
object-specific memory这个区域专门用来存放pyobject。你也许已经知道了,python中所有的数据类型比如int,dict,str等都是一个对象,叫做pyobject。当我们在python中创建一个变量比如dict时,cpython就会在堆区的上半部分(object-specific memory)中分配一块内存,创建一个pyobject,这个pyobject用来存放我们的dict。
python core:所有非pyobject的内存请求都在这里分配的。
所以你会发现,python中所有的内存同样是解释器在自己的堆上分配的。
一展看透显示触控材料发展现状 | 2023深圳国际全触与显示展汇聚产业热点新品!
华为手机全球份额反超苹果 小米去年全球投资金额达到58亿元
剑指Offer(36):两个链表的第一个公共结点
2020年7月,汽车产销保持两位数涨幅;乘用车延续增长态势
PCB设计总有几个阻抗没法连续的地方该怎么办?
程序员眼里的内存(中)
比特币常见的错误怎样去避免
谈谈新型存储
英国研发低成本碳基电催化剂可有效提升燃料电池的能量密度
vr头戴式设备的视野是什么?
模拟量0-5v0-10v4-20mA转光端机光纤收发器
一文了解英特尔CPU接口的发展史
Teledyne新型SWIR线扫描相机可超出可见光的范围缺陷
又准又稳,让采样事半功倍!纳芯微推出全新隔离式Sigma-Delta调制器隔离采样芯片
小蚁室内云台摄像机H20GB即将发布 售价149元
电流互感器的接线方法
高德地图出骑行导航 骑友们可以爽了
重新定义模块化产品,无线取代有线连接器势不可挡!
采用高端智能视频服务器设计油田周边监控防范系统
华为为巴西部署5G 将延迟数年