`
jack
  • 浏览: 392486 次
  • 来自: 温州
社区版块
存档分类
最新评论

项目事故和安全语言

阅读更多
  有个从表面上看起来很奇怪的事实是:司机戴安全带比不带安全带时发生的行车事故要多。不带安全带时,司机死亡率比较高,但是带了安全带时,司机死亡率是降低了,但是行人死亡率却提高了。

  不带安全带时,司机需要谨慎缓慢的开车,带了安全带,司机却会 更快,更放肆地开车。
这个方面的资料 可以看下 Automobile Safety Regulation and the Incentive to Drive Recklessly:Evidence from NASCAR 和Automobile Safety Regulation

  编程语言,从一种不安全语言(比如c/c++)发展到一种相对安全语言(比如C#)时,语言上安全程度提高,并不因此能够大量的减少项目事故,只能减少对开发人员威胁程度而已。而对于客户平均威胁程度却是提高了。

   安全性高的语言,就像司机有了安全带一样,使得原本那些担心做不好而使得自己饭碗不保地不合格开发人员更加-------放肆了。

  同样的项目 用c/c++开发,开发人员知道c/c++极易出错,开发速度故而缓慢,谨慎。尽量避免在开始就出错。项目运行时,初期出错。小错误还好,如果是系统崩溃之类的错误,有麻烦的是开发人员。

  用C#开发,则因为C#和.net平台有各种各样的保证机制。相对不容易出错,比如c/c++中最麻烦的内存,资源回收和指针问题。项目运行初期也很难发现.等运行一段时间之后,出错了很有可能就需要重新设计了。

  开发语言虽安全,请各位"新人司机"缓慢,谨慎驾驶。
分享到:
评论
139 楼 netpcc 2007-07-02  
fustic 写道
netpcc 写道
看来大家对硬件和操作系统有不少误解啊。
以下说明仅仅针对x86CPU和32位操作系统。X64及64位操作系统我也不清楚,不过差别不会太大。

1. X86CPU一旦进入保护模式,就不会直接访问物理内存了。
必须在分段,分页或者段页式中选择一种。因此内核中的地址也是虚拟的。要经过GDT/LDT/TLB转换,才能得到物理内存的地址。


你说的这个物理、虚拟的概念跟大家讨论的有些出入,
一般我们认为内核逻辑地址就是物理地址,它跟物理地址之间
有一个简单的映射关系
用户级程序看到的地址肯定是虚拟地址,即使它看起来是连续的,
实际在内核里面也是以链表形式组织的

内核中除了页面管理这一小部分,其他模块使用的内存,同样是按照分页的方式来管理的。和应用程序有什么区别?
fustic 写道

netpcc 写道

2. 内存和内存地址是2个概念,都是很重要的资源。对于内核来说,有4GB的内存地址和物理内存数目的物理内存。对于用户态程序来说,有2-3GB的内存地址(因OS而异)。物理内存数不确定,由操作系统动态分配。
当程序向OS要求分配内存时,仅仅得到了内存地址。而不是物理内存的。在真正使用内存时,才会该地址才会被映射到物理内存,或者是交换文件。


linux 32bit x86cpu 下面,一般是 1:3,用户级内存寻址空间为3G,内核为1G,虽然每个用户级程序
都会看到3G的可用空间,但是并不等于这些data都在内存中,有些可能被OS swap到交换区,但这些细节是对
程序隐藏的。
内核级所看到的1G内存一般是唯一映射到系统物理地址上的,只是为了开发方便,有时内核里面也会把一些不连续的物理内存映射到一个虚拟地址上。

最上端的1G内存,并不是给内核用的。通常是映射到共享模块和系统API。一旦进入Ring0就使用内核自己的地址空间了。
fustic 写道

netpcc 写道


3.为什么说操作系统可以在进程结束时回收所有的内存。因为OS有所有的虚拟地址到物理内存的映射关系。所以很容易回收所有的物理内存。一个成熟的OS这方面是不会有BUG。但是有很多资源是OS无法自动回收的(或者说很难)。比如进程间通讯用的共享内存,内核态的同步对象,内核态的时钟等等。应为这些都可以被多个进程共享,撤销一个进程时很难判断是否能被回收。

4. 关于内存碎片。内存碎片本质上是内存地址不足造成的。举个极限例子,假设进程需要256M内存,然后把这部分内存按照每16K使用4K的方式排列,那么就无法分配到12K以上的内存了。不是应为没有物理内存,而是没有连续的内存空间了。因此对于64位系统,应为相对于物理内存而言有极大的地址资源,基本上不用担心内存碎片的问题了。

5. C++的memory leak。综上所述,C++的memory leak最主要消耗的是内存地址资源,而不是物理内存。因为被泄漏的内存不会再被访问,因此OS会把它们交换到SWAP里去。进程结束时,地址空间当然会被全部回收了。而大部分物理内存在此之前早就被OS回收了。


内存泄漏、内存溢出一般都是应用层(用户级空间)的概念,操作系统会维护每个进程的资源(内存、文件句柄等),所以进程一旦退出,所有消耗的资源都会回收

内存碎片则在内核和用户级空间都会存在。

所有分配出去的资源都应该回收,没错,理论上是这样。但实际上很难全部回收,或多或少都存在着一些资源泄漏。应为对有些资源的跟踪相当困难。特别是现在的OS将很多服务都放在Ring0来运行,就更难避免资源泄漏了。
138 楼 fustic 2007-07-01  
netpcc 写道
看来大家对硬件和操作系统有不少误解啊。
以下说明仅仅针对x86CPU和32位操作系统。X64及64位操作系统我也不清楚,不过差别不会太大。

1. X86CPU一旦进入保护模式,就不会直接访问物理内存了。
必须在分段,分页或者段页式中选择一种。因此内核中的地址也是虚拟的。要经过GDT/LDT/TLB转换,才能得到物理内存的地址。


你说的这个物理、虚拟的概念跟大家讨论的有些出入,
一般我们认为内核逻辑地址就是物理地址,它跟物理地址之间
有一个简单的映射关系
用户级程序看到的地址肯定是虚拟地址,即使它看起来是连续的,
实际在内核里面也是以链表形式组织的

netpcc 写道

2. 内存和内存地址是2个概念,都是很重要的资源。对于内核来说,有4GB的内存地址和物理内存数目的物理内存。对于用户态程序来说,有2-3GB的内存地址(因OS而异)。物理内存数不确定,由操作系统动态分配。
当程序向OS要求分配内存时,仅仅得到了内存地址。而不是物理内存的。在真正使用内存时,才会该地址才会被映射到物理内存,或者是交换文件。


linux 32bit x86cpu 下面,一般是 1:3,用户级内存寻址空间为3G,内核为1G,虽然每个用户级程序
都会看到3G的可用空间,但是并不等于这些data都在内存中,有些可能被OS swap到交换区,但这些细节是对
程序隐藏的。
内核级所看到的1G内存一般是唯一映射到系统物理地址上的,只是为了开发方便,有时内核里面也会把一些不连续的物理内存映射到一个虚拟地址上。


netpcc 写道


3.为什么说操作系统可以在进程结束时回收所有的内存。因为OS有所有的虚拟地址到物理内存的映射关系。所以很容易回收所有的物理内存。一个成熟的OS这方面是不会有BUG。但是有很多资源是OS无法自动回收的(或者说很难)。比如进程间通讯用的共享内存,内核态的同步对象,内核态的时钟等等。应为这些都可以被多个进程共享,撤销一个进程时很难判断是否能被回收。

4. 关于内存碎片。内存碎片本质上是内存地址不足造成的。举个极限例子,假设进程需要256M内存,然后把这部分内存按照每16K使用4K的方式排列,那么就无法分配到12K以上的内存了。不是应为没有物理内存,而是没有连续的内存空间了。因此对于64位系统,应为相对于物理内存而言有极大的地址资源,基本上不用担心内存碎片的问题了。

5. C++的memory leak。综上所述,C++的memory leak最主要消耗的是内存地址资源,而不是物理内存。因为被泄漏的内存不会再被访问,因此OS会把它们交换到SWAP里去。进程结束时,地址空间当然会被全部回收了。而大部分物理内存在此之前早就被OS回收了。


内存泄漏、内存溢出一般都是应用层(用户级空间)的概念,操作系统会维护每个进程的资源(内存、文件句柄等),所以进程一旦退出,所有消耗的资源都会回收

内存碎片则在内核和用户级空间都会存在。
137 楼 netpcc 2007-06-29  
看来大家对硬件和操作系统有不少误解啊。
以下说明仅仅针对x86CPU和32位操作系统。X64及64位操作系统我也不清楚,不过差别不会太大。

1. X86CPU一旦进入保护模式,就不会直接访问物理内存了。
必须在分段,分页或者段页式中选择一种。因此内核中的地址也是虚拟的。要经过GDT/LDT/TLB转换,才能得到物理内存的地址。

2. 内存和内存地址是2个概念,都是很重要的资源。对于内核来说,有4GB的内存地址和物理内存数目的物理内存。对于用户态程序来说,有2-3GB的内存地址(因OS而异)。物理内存数不确定,由操作系统动态分配。
当程序向OS要求分配内存时,仅仅得到了内存地址。而不是物理内存的。在真正使用内存时,才会该地址才会被映射到物理内存,或者是交换文件。

3.为什么说操作系统可以在进程结束时回收所有的内存。因为OS有所有的虚拟地址到物理内存的映射关系。所以很容易回收所有的物理内存。一个成熟的OS这方面是不会有BUG。但是有很多资源是OS无法自动回收的(或者说很难)。比如进程间通讯用的共享内存,内核态的同步对象,内核态的时钟等等。应为这些都可以被多个进程共享,撤销一个进程时很难判断是否能被回收。

4. 关于内存碎片。内存碎片本质上是内存地址不足造成的。举个极限例子,假设进程需要256M内存,然后把这部分内存按照每16K使用4K的方式排列,那么就无法分配到12K以上的内存了。不是应为没有物理内存,而是没有连续的内存空间了。因此对于64位系统,应为相对于物理内存而言有极大的地址资源,基本上不用担心内存碎片的问题了。

5. C++的memory leak。综上所述,C++的memory leak最主要消耗的是内存地址资源,而不是物理内存。因为被泄漏的内存不会再被访问,因此OS会把它们交换到SWAP里去。进程结束时,地址空间当然会被全部回收了。而大部分物理内存在此之前早就被OS回收了。
136 楼 fustic 2007-06-28  
ddd 写道
》虚拟内存是连续的,未必物理内存也是连续的。

如何保证物理内存是连续的?

linux内核里面有专门的api,可以申请连续的物理内存,一般好像是给DMA使用的,
很久之前看过,如果有兴趣,不妨翻一下<understanding linux kernel>,还是比较
通俗易懂的
135 楼 fustic 2007-06-28  
bigpanda 写道
fustic 写道
什么时候会遇到内存碎片?



内存碎片和硬盘碎片原理差不多。

举个例子,程序刚开始运行的时候,连续五次向Heap申请内存,每次1k,然后释放了第二个,第四个,然后再申请4k内存,这4k内存不是连续的,而是分成了三片,在原来的第二块,第四块和第五块后面。这样在这4k里面读写数据就会在内存里跳来跳去,影响性能。



上面这段有错误。

举个例子,程序刚开始运行的时候,连续五次向Heap申请内存,每次1k,然后释放了第二个,第四个,然后再申请4k内存,这4k内存只能放在原来的第五块后面。如果将来申请的内存都大于1k,中间的第二块,第四块就成碎片。

http://en.wikipedia.org/wiki/Fragmentation_%28computer%29



推荐几本书,

Programming Applications for Microsoft Windows, 4th Edition, by Jeffrey Richter
这本书对Win32 API讲的非常好,process, thread, job, virtual memory, heap, thread stack都讲的很清楚,概括了操作系统的很大一部分,结合API讲,比较实际,比光讲理论的操作系统教科书好理解。操作系统教科书里面谈到Windows NT/2k/XP都推荐这本书。

Memory Management: Algorithms and Implementations In C/C++,Bill Blunden,结合处理器讲,有些东西别的书里都没写,比如操作系统如何实现Atomic操作,semaphore是怎么实现的。

IA-32 Intel® Architecture Software Developer's Manual Volume 3A: System Programming Guide, Part 1,可下载:
http://www.intel.com/design/Pentium4/manuals/253668.htm

操作系统是和处理器紧密结合的,唇齿相依,把这本书翻一遍可以大大加深理解,要能精通就是大牛了,可以在Linux,BSD内核里折腾了。


原理我也知道,但是从来没试过因为内存碎片导致应用异常的情况(也许是遇到了也不知道是怎么回事,呵呵)
一般写7*24程序时,会尽量避免使用heap,而使用栈空间,或者一次分配足够的内存(也许嵌入式系统不能这样干)
另外,据说stl有自己的内存管理系统,不知道是否会做一些内存合并的功能,或者使用一些非glibc的malloc替代品,有很多高性能的内存分配库,比如google的,或者其他的,都会有很强的内存管理能力。

不要太依靠操作系统,它是面向通用程序的,不会为专门的程序做优化,呵呵

134 楼 fixopen 2006-10-24  
Elminster对于GC仍然那么感兴趣。其实就memory来说,其分配和回收策略确实千变万化,考虑不同程序的不同局部性和临时性为的时候更是如此。从一个程序内部考虑其cache missing rate其实是不够准确的,必定得涉及到别的进程的行为,其实,memory这个资源的使用方式,最合理明智的决定只能是综合参考了进程和OS双方的信息之后才能给出的。Activation Scheduler对于CPU这个资源作的事情,也可以进一步推广到memory上。
133 楼 JeffreyHsu 2006-10-19  
这纯属态度问题,安全的语言难道就不能谨慎了么?
难道就不写单元测试了?
不能因噎废食啊
132 楼 Elminster 2006-10-10  
potian 写道
确实说得思路不太清楚,我写的时候把想到的不同的收集方式的缩并相关所有问题都堆上来了。是否缩并和节点复制/标记是正交的问题。节点复制式(需要半空间)只不过具有自然的缩并效果,它需要的是复制的开销,当然可以说不是额外的开销。另外的还有标记缩并算法。标记缩并通常不需要清扫的,而是标记+缩并,不是标记清扫+缩并。

除了复制自然缩并外,缩并算法可以还采用诸如双指针、迁移地址、表方法和穿线法等等。这些缩并算法一般需要遍历存活数据结构多次。而且任意型的缩并算法(如双指针)本身在缩并的时候并没有考虑局部性。线性的方法(如节点复制的)从引用关系考虑局部性。滑动方法从分配位置考虑局部性。不能说缩并局部性一定就好了。

关于我说的生命周期的相似性,就是说同时创建的对象通常来说是同时存活或同时死亡的,并且它们极大的可能占据相近的空间,因此,要么局部性很好,要么一下子都被收集了。不大可能造成程序的工作集分布在不同的页面上,页面频繁换进换出。这方面文献应该也不会太少。


你这样说就清楚多了。

确实缩并的算法不一定有优势,它们最大的问题是无论如何都要移动对象,而且多半会触及被收集的堆中大部分甚至所有的对象。因此这类算法在执行收集过程中要访问堆的不同部分,这往往导致更多的缺页错误。但另一方面,这类算法在内存分配效率的优势也的确不容忽视。局部性方面么,要看具体实现,简单地缩并算法(比如你提到的双指针算法)可能反而恶化了程序的局部性,但也有充分考虑内存访问局部性乃至 cache 命中的实现,甚至存在一些实现是有“训练”过程的,这个就又很难说 ……

总之,我仍然认为,对于这么复杂而且不确定的课题,是很难下一个明确的结论的 …………
131 楼 potian 2006-10-10  
确实说得思路不太清楚,我写的时候把想到的不同的收集方式的缩并相关所有问题都堆上来了。是否缩并和节点复制/标记是正交的问题。节点复制式(需要半空间)只不过具有自然的缩并效果,它需要的是复制的开销,当然可以说不是额外的开销。另外的还有标记缩并算法。标记缩并通常不需要清扫的,而是标记+缩并,不是标记清扫+缩并。

除了复制自然缩并外,缩并算法可以还采用诸如双指针、迁移地址、表方法和穿线法等等。这些缩并算法一般需要遍历存活数据结构多次。而且任意型的缩并算法(如双指针)本身在缩并的时候并没有考虑局部性。线性的方法(如节点复制的)从引用关系考虑局部性。滑动方法从分配位置考虑局部性。不能说缩并局部性一定就好了。


关于我说的生命周期的相似性,就是说同时创建的对象通常来说是同时存活或同时死亡的,并且它们极大的可能占据相近的空间,因此,要么局部性很好,要么一下子都被收集了。不大可能造成程序的工作集分布在不同的页面上,页面频繁换进换出。这方面文献应该也不会太少。

130 楼 Elminster 2006-10-10  
potian 写道
实际上复杂的内存分配策略并一定能够减少内存分配碎片,譬如论文中提到很多情况下first fit比best fit更加好,所以也不意味着产生内存碎片越少的算法复杂度越高。

缩并的算法虽然可以直接通过指针移动快速分配内存,而且看起来局部性更好,但是相邻内存空间生命周期的近视性使得局部性的意义不大,而缩并操作时造成缺页的可能性更大,因为需要缩并、移动,而非缩并可能只需要操作bitmap即可。另外需要半空间也是个大问题。另外,缩并本身也是需要成本的。应该说,缩并算法从最早的压倒性优势到目前很多新的GC没有采用的趋势还是比较明显的,尽管复杂的GC可能会进行适应性的算法。

当然,这些都和程序的具体内存行为有关。


确实,复杂的内存分配策略并不一定能够减少内存分配碎片,但这与我的观点并不矛盾:不能因为某些内存分配机制产生的内存碎片数量极少就得出问题已经解决的结论,还是应该权衡考虑内存分配的耗时等其他因素。

关于缩并的垃圾收集机制,你的说法让我有点迷惑,似乎应该澄清一下概念。

能够实现“缩并”效果的垃圾收集算法有两类,一类是在标记-清扫算法基础上加上缩并阶段,另一类是基于节点复制的算法。你所说的缩并指的是哪一种?前一类算法有额外的缩并阶段,所以“缩并本身也是需要成本的”,但不需要半空间;后一类算法“需要半空间”,但是缩并效果是收集过程的自然结果,不需要额外的成本。你两条都提到了,让我有些迷惑。另外,你说“相邻内存空间生命周期的近似性使得局部性的意义不大”,不是很清楚你这里的所指,可以解释一下么?

不过对于带有缩并效果的算法执行过程本身可能造成缺页这一点,我完全赞同,并且认为这可能是这一大类算法最大的问题。
129 楼 potian 2006-10-10  
实际上复杂的内存分配策略并一定能够减少内存分配碎片,譬如论文中提到很多情况下first fit比best fit更加好,所以也不意味着产生内存碎片越少的算法复杂度越高。

缩并的算法虽然可以直接通过指针移动快速分配内存,而且看起来局部性更好,但是相邻内存空间生命周期的近视性使得局部性的意义不大,而缩并操作时造成缺页的可能性更大,因为需要缩并、移动,而非缩并可能只需要操作bitmap即可。另外需要半空间也是个大问题。另外,缩并本身也是需要成本的。应该说,缩并算法从最早的压倒性优势到目前很多新的GC没有采用的趋势还是比较明显的,尽管复杂的GC可能会进行适应性的算法。

当然,这些都和程序的具体内存行为有关。
128 楼 Elminster 2006-10-10  
potian 写道
实际上,内存碎片并没有想象的那么厉害。内存管理技术的发展已经让碎片的几率大大下降。譬如linux的malloc和free采用的是DougLea的内存分配器。

关于这一点,对垃圾收集感兴趣的人一般都会读过几片相关的论文。Paul R. Wilson 有两篇论文专门研究这个问题。调查和讨论了几乎所有现在的所有内存分配策略。

早期很多人都认为内存碎片会是一个大问题,但是经过对很多现实程序内存管理行为的跟踪、统计和分析,近年来越来越形成共识的是,如果采用比较现代的分配器,实际上内存碎片是很少的。缩并式的垃圾收集算法也因为这一点显得越来越没有优势。

The memory fragmentation problem: solved?


这个 …… 可能暂时还没有法子下结论吧。比较 sophisticate 的内存分配算法确实可以在很大程度上减轻内存碎片的出现,但是也有可能导致一个内存分配操作需要消耗更多的时钟周期。所以对于内存分配策略来说,我们不能单纯考察一面,要考虑到内存碎片和分配操作耗时的权衡。据我所知,似乎也还没有一个完全的定论。类似的,对于缩并式的垃圾收集算法,也必须考虑到内存分配效率的问题。如果内存区域是连续的,那么一次内存分配可以简单地通过移动一个指针来完成,这是一个不可忽视的优势。除此之外,缩并式垃圾收集算法可能可以(如果实现方式适当的话)提升程序内存访问的局部性,这也是一个优势。

不过老实说,对于内存分配这样一般性的操作来说,恐怕很难说哪一种策略是更优的,要考虑的因素实在太多了 ……
127 楼 bigpanda 2006-10-10  
多谢stephen指点,我搞错了。

给个链接:http://en.wikipedia.org/wiki/Fragmentation_%28computer%29

里面谈到了三种fragmentation,external fragmentation, internal fragmentation和data fragmentation。

我上大学的时候,上过一门计算机高级架构的课,讲课的教授特别牛,曾经是Sun的首席设计师。他重点讲了data fragmentation引起的处理器二级缓存miss的问题。我对这个data fragmentation留下的印象特别深,所以想当然了一把。data fragmentation看来会由linked list引起,不会由malloc引起。

我还把potian说的The Memory Fragmentation Problem: Solved?找来看了一遍, 里面提到的Allocator都是把malloc要申请的内存块一整块返回的。Paul R. Wilson的另一篇文章太长了,以后再读。

我把原来写的编辑一下。

126 楼 potian 2006-10-09  
实际上,内存碎片并没有想象的那么厉害。内存管理技术的发展已经让碎片的几率大大下降。譬如linux的malloc和free采用的是DougLea的内存分配器。

关于这一点,对垃圾收集感兴趣的人一般都会读过几片相关的论文。Paul R. Wilson 有两篇论文专门研究这个问题。调查和讨论了几乎所有现在的所有内存分配策略。

早期很多人都认为内存碎片会是一个大问题,但是经过对很多现实程序内存管理行为的跟踪、统计和分析,近年来越来越形成共识的是,如果采用比较现代的分配器,实际上内存碎片是很少的。缩并式的垃圾收集算法也因为这一点显得越来越没有优势。

The memory fragmentation problem: solved?
125 楼 ddd 2006-10-09  
》另外,内存的碎片的意思是这种吧:
你看的这个是正确的。
124 楼 stephen 2006-10-09  
bigpanda 写道


上面写的这一段代码中用户进程操作的内存地址都是虚拟地址,操作系统会把这些虚拟地址映射到物理地址上(需要CPU的协作)。操作系统为了简化用户程序员的工作量,作了大量的工作,隐藏了很多细节,虚拟内存是连续的,未必物理内存也是连续的。



bigpanda 能否给出一些描述这种情况的 url ?

刚才去 google 了一把,查到的结果和你描述的情况是相反的。比如以下这个 url 中的描述:
http://acm.pku.edu.cn/JudgeOnline/problem?id=1193

引用

经典的内存分配过程是这样进行的:
1. 内存以内存单元为基本单位,每个内存单元用一个固定的整数作为标识,称为地址。地址从0开始连续排列,地址相邻的内存单元被认为是逻辑上连续的。我们把从地址i开始的s个连续的内存单元称为首地址为i长度为s的地址片。
2. 运行过程中有若干进程需要占用内存,对于每个进程有一个申请时刻T,需要内存单元数M及运行时间P。在运行时间P内(即T时刻开始,T+P时刻结束),这M个被占用的内存单元不能再被其他进程使用。
3、假设在T时刻有一个进程申请M个单元,且运行时间为P,则:
1. 若T时刻内存中存在长度为M的空闲地址片,则系统将这M个空闲单元分配给该进程。若存在多个长度为M个空闲地址片,则系统将首地址最小的那个空闲地址片分配给该进程。
2. 如果T时刻不存在长度为M的空闲地址片,则该进程被放入一个等待队列。对于处于等待队列队头的进程,只要在任一时刻,存在长度为M的空闲地址片,系统马上将该进程取出队列,并为它分配内存单元。注意,在进行内存分配处理过程中,处于等待队列队头的进程的处理优先级最高,队列中的其它进程不能先于队头进程被处理。
现在给出一系列描述进程的数据,请编写一程序模拟系统分配内存的过程。


另外,内存的碎片的意思是这种吧:
http://www.itisedu.com/07/200608230919099.asp

引用

标准C库函数malloc()和free()可在任意的时间段中,为应用分配任意大小的内存块。随着内存块的使用和释放,在整个内存区域中,分配给堆栈的存储区将混杂着许多正在使用或已经释放的存储块,而未被使用的任何小块内存区将变得无法使用。例如,某个应用要求堆栈分配30字节,如果堆栈中只有20个长度为3字节的小存储块(总共为60字节),那么堆栈仍然无法为该应用分配内存,因为所需的30字节必须是连续的。


http://www.ecnchina.com/Article_Show.asp?ArticleID=4132

引用

  内存分配程序浪费内存的基本方式有三种:即额外开销、内部碎片以及外部碎片(图 1)。
  内存分配程序需要存储一些描述其分配状态的数据。这些存储的信息包括任何一个空闲内存块的位置、大小和所有权,以及其它内部状态详情。一般来说,一个运行时间分配程序存放这些额外信息最好的地方是它管理的内存。
  内存分配程序需要遵循一些基本的内存分配规则。例如,所有的内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址。内存分配程序把仅仅预定大小的内存块分配给客户,可能还有其它原因。当某个客户请求一个 43 字节的内存块时,它可能会获得 44字节、48字节 甚至更多的字节。由所需大小四舍五入而产生的多余空间就叫内部碎片。
  外部碎片的产生是当已分配内存块之间出现未被使用的差额时,就会产生外部碎片。例如,一个应用程序分配三个连续的内存块,然后使中间的一个内存块空闲。内存分配程序可以重新使用中间内存块供将来进行分配,但不太可能分配的块正好与全部空闲内存一样大。倘若在运行期间,内存分配程序不改变其实现法与四舍五入策略,则额外开销和内部碎片在整个系统寿命期间保持不变。虽然额外开销和内部碎片会浪费内存,因此是不可取的,但外部碎片才是嵌入系统开发人员真正的敌人,造成系统失效的正是分配问题。


而不是

bigpanda 写道

内存碎片和硬盘碎片原理差不多。

举个例子,程序刚开始运行的时候,连续五次向Heap申请内存,每次1k,然后释放了第二个,第四个,然后再申请4k内存,这4k内存不是连续的,而是分成了三片,在原来的第二块,第四块和第五块后面。这样在这4k里面读写数据就会在内存里跳来跳去,影响性能。
123 楼 crmky 2006-10-09  
top和lsof这些在linux上面都是通过proc文件系统来得到这些信息的,所以最后还是要看/proc/meminfo。

不过robbin已经分析过这个文件了,我奇怪的是难道在这个虚拟文件中,3G内存无影无踪么?
122 楼 ddd 2006-10-09  
》虚拟内存是连续的,未必物理内存也是连续的。

如何保证物理内存是连续的?
121 楼 bigpanda 2006-10-05  
stephen 写道
在 c/c++ 的内存分配中,内存分配一定是连续的。

char * p = ( char * ) malloc( 8092 );
for( int i = 0; i < 8092; i++ ) {
  *p = 'a';
  printf( "%p\n", p++ );
}


无论如何,上面的代码输出的地址总应该是连续的。而且这些输出的地址就应该是这次 malloc 分配的结果。malloc 分配的内存是连续的,这是 c/c++ 依赖的一个基本假设。上面的这段代码在 c/c++ 中是非常常见的。


上面写的这一段代码中用户进程操作的内存地址都是虚拟地址,操作系统会把这些虚拟地址映射到物理地址上(需要CPU的协作)。操作系统为了简化用户程序员的工作量,作了大量的工作,隐藏了很多细节,虚拟内存是连续的,未必物理内存也是连续的。

当然Heap内存管理实行方式也有很多种,大大不一样。Bitmapped Allocation,Sequential Fit,Segregated Lists造成的后果和影响都不一样,这还只是教科书上比较简单的方法。Linux, Windows, BSD里面有更复杂的实行方式。我举的碎片例子不俱有代表性。只是在使用某些Heap管理方式的时候会出现。
120 楼 stephen 2006-10-05  
bigpanda 写道

举个例子,程序刚开始运行的时候,连续五次向Heap申请内存,每次1k,然后释放了第二个,第四个,然后再申请4k内存,这4k内存不是连续的,而是分成了三片,在原来的第二块,第四块和第五块后面。这样在这4k里面读写数据就会在内存里跳来跳去,影响性能。


不同意这个观点。内存分配和磁盘空间分配是不同的。上面的这种情况是磁盘空间分配的做法。
在 c/c++ 的内存分配中,内存分配一定是连续的。

char * p = ( char * ) malloc( 8092 );
for( int i = 0; i < 8092; i++ ) {
  *p = 'a';
  printf( "%p\n", p++ );
}


无论如何,上面的代码输出的地址总应该是连续的。
而且这些输出的地址就应该是这次 malloc 分配的结果。
malloc 分配的内存是连续的,这是 c/c++ 依赖的一个基本假设。
上面的这段代码在 c/c++ 中是非常常见的。

相关推荐

    go语言项目实战源码资料

    理解如何创建和管理goroutines,以及如何通过channels进行安全的数据通信是Go语言的一大特色。 4. **网络编程**:Go语言提供了强大的net和http标准库,使得网络编程变得简单。你可能会学习到如何创建HTTP服务器,...

    Go 语言课程和项目源码

    非类型安全指针 Go语言切片 作业 Day04 重点知识回顾与答疑 Day05 字符串 Go语言Map Go语言接口 错误处理 空接口与类型断言 函数式编程 面向对象 面向接口 作业 Day06 IO操作 反射与AST 文件读写 包与工程 单元测试...

    基于python开发的Ai助手项目源码(集成了语言识别、图像识别和自然语言处理).zip

    基于python开发的Ai助手项目源码(集成了语言识别、图像识别和自然语言处理).zip基于python开发的Ai助手项目源码(集成了语言识别、图像识别和自然语言处理).zip基于python开发的Ai助手项目源码(集成了语言识别、...

    go开源项目大全(目前最完整)

    因此我自己根据go语言中文社区提供的资料,还有互联网企业架构设计中的常见组件分类, 共精心挑选了100多个开源项目(项目不限于在github开源的项目), 分成以下十几个大类。 这个项目可以理解为互联网IT人打造的...

    PanUmlTools项目一个java语言实现的

    Java是一种广泛应用于开发各种类型应用程序的编程语言,具有跨平台、面向对象、高性能和安全性等特点。Java项目可以用于开发各种类型的应用程序,包括桌面应用程序、Web应用程序、移动应用程序和企业级应用程序等。 ...

    (完整版)项目部安全管理体系.pdf

    3. 安全标准与程序:制定工程项目安全操作规范和流程,包含紧急应对预案和安全检查制度。 4. 安全培训与教育:定期对员工进行安全知识与技能的培训,提升安全意识和应急处理能力。 5. 安全监督与检查:开展日常安全...

    Perl语言编程.pdf

    * 不适合大型项目:Perl语言不适合用于大型项目,例如操作系统、数据库管理系统等。 * 安全性欠缺:Perl语言的安全性欠缺,需要用户小心地编写代码来避免安全问题。 Perl语言的发展历史: * 1987年,Larry Wall...

    公路水运项目工程施工企业主要负责人和安全生产管理人员考核大纲模拟试题库.doc

    同时,综合能力测试了语言沟通、规范理解、道德评价、心理承受和突发事件处理等公共基本能力,以及安全法规执行、监督、隐患排查、事故分析和信息处理等安全管理行为能力。 2. 法律法规及规章规程:考核人员需熟悉...

    基于Go语言和Gin框架的完善Web项目骨架.zip

    通过深入学习和使用这个项目骨架,开发者不仅可以掌握Go语言和Gin框架的基本用法,还能了解到如何优化应用性能,实现高效、安全的企业级服务。同时,通过研究`skeleton-main`文件,可以学习到如何组织代码结构,处理...

    建设项目的安全和职业健康“三同时”管理.pdf

    1. 建设项目安全管理制度:公司需建立完善的建设项目安全管理制度,涵盖从可行性研究、设计、施工到验收的全过程,确保所有环节遵循国家和行业的安全标准。 2. 可行性研究阶段:在项目启动初期,需对可能存在的危险...

    基于Unity3d和C#语言开发的回合制RPG游戏项目源码+sln解决方案(课程作业).zip

    1.项目代码功能经验证ok,确保稳定可靠运行。欢迎下载使用!在使用过程中,如有问题或建议,请及时私信沟通。 2.主要针对各个计算机相关...基于Unity3d和C#语言开发的回合制RPG游戏项目源码+sln解决方案(课程作业).zip

    基于Java语言的福建师范大学计算机与信息安全学院2021级软件工程&新意科技项目实训课程第四组设计源码

    该项目是福建师范大学计算机与信息安全学院2021级软件工程与新意科技项目实训课程的第四组设计源码,采用Java语言开发,并集成了Vue、JavaScript、CSS和HTML等多语言技术。项目包含28个文件,具体分布为7个Vue文件、...

    Rust语言相关项目的示例

    Rust是一种系统级编程语言,由Mozilla研发,旨在提供内存安全、并发性能和速度的完美平衡。这个压缩包文件“Rust语言相关项目的示例”很可能是包含了一系列使用Rust编程语言开发的实际项目实例,旨在帮助学习者深入...

    锂日历记事本-Gin后端项目,基于go语言Gin框架开发.zip

    它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如...

    基于深度学习的建筑安全事故预防策略综述.pdf

    其在计算机视觉、自然语言处理和语音识别等多个技术领域的成功应用,为建筑安全事故的预防提供了新的思路。深度学习技术能够通过多层神经网络结构,对复杂的数据进行有效建模和特征提取,从而实现对施工现场的自动...

    技术服务方案、项目实施方案及项目管理组织机构.pdf

    这个项目的技术服务方案、项目实施方案及项目管理组织机构全面覆盖了工程的各个环节,从前期的技术准备到后期的施工管理,都旨在保证工程的安全、质量和进度,同时注重环境保护和文明施工,展现了严谨的项目管理流程...

    基于Energyplus、Golang、React和R语言的建筑能耗计算系统源码+项目说明.zip

    基于Energyplus、Golang、React和R语言的建筑能耗计算系统源码+项目说明.zip 【说明】 【1】项目代码完整且功能都验证ok,确保稳定可靠运行后才上传。欢迎下载使用!在使用过程中,如有问题或建议,请及时私信沟通,...

    网络安全大语言模型.zip

    网络安全大语言模型 ----- 毕业设计,课程设计,项目源码均经过助教老师测试,运行无误,欢迎下载交流 ----- 下载后请首先打开README.md文件(如有),某些链接可能需要魔法打开。 ----- 毕业设计,课程设计,项目...

    信息安全大作业-CA系统的设计和实现源码(电子认证服务系统-数字证书数字签名python语言)+项目详细说明

    信息安全大作业-CA系统的设计和实现源码(电子认证服务系统-数字证书数字签名python语言)+项目详细说明, CA代表Certificate Authority。也就是电子认证服务或机构,为电子签名相关各方提供真实性和可靠性验证,是...

Global site tag (gtag.js) - Google Analytics