目录

堆分配的内存空间是否连续

问题的引出

堆包含一个链表来维护已用和空闲的内存块。在堆上新分配(用 new 或者 malloc)内存是从空闲的内存块中找到一些满足要求的合适块。所以可能让人觉得只要有很多不连续的零散的小区域,只要总数达到申请的内存块,就可以分配。

但事实上是不行的,这又让人觉得是不是零散的内存块不能连接成一个大的空间,而必须要一整块连续的内存空间才能申请成功呢。

答案

申请和释放许多小的块可能会产生如下状态:在已用块之间存在很多小的空闲块。进而申请大块内存失败,虽然空闲块的总和足够,但是空闲的小块是零散的,不能满足申请的大小,。这叫做“堆碎片”。 当旁边有空闲块的已用块被释放时,新的空闲块会与相连的空闲块合并成一个大的空闲块,这样就可以有效的减少"堆碎片"的产生。 堆分配的空间在逻辑地址上是连续的,但在物理地址上是不连续的(因为采用了页式内存管理,windows下有段机制、分页机制),如果逻辑地址空间上已经没有一段连续且足够大的空间,则分配内存失败。

———————————————— 版权声明:本文为CSDN博主「Jason Gel」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/jin13277480598/article/details/54409543/

进一步阐述

假设操作系统给jvm进程分配的物理块编号分别是3、5、8,物理地址分别是300-399,500-599,800-899;jvm进程作为应用程序逻辑地址是000-299,分配对象时必须保证有连续的逻辑地址空间可以分配,虽然它们在物理空间上是不连续的。

假设我们是按字节寻址,现在jvm逻辑地址只剩下000-002 3个字节(上文中说的堆碎片,是逻辑地址概念上的碎片)和 100-102 3个字节 共计 6个字节的空闲空间,现在要分配一个对象大小是5个字节,因为剩余空间的逻辑地址不连续所以分配内存失败。

JVM规范中规定,Java堆可以处于逻辑上连续,物理上不连续的内存空间中,就像我们的磁盘一样。如果在堆中没有内存分配给对象实例,并且堆也无法扩展,也会抛出OutOfMemoryError异常。