从C和C++内存管理来谈谈JVM的垃圾回收算法设计-上 引言 C内存模型 malloc堆内存分配过程 malloc为什么结合使用brk和mmap malloc如何通过内存池管理Heap区域 垃圾收集器...在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。...本部分内容参考文献 malloc为什么结合使用brk和mmap brk: 一般如果用户分配的内存小于 128 KB,则通过 brk() 申请内存。...(就是自己手动释放内存) 隐式分配器:要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块。隐式分配器也叫做垃圾收集器,而自动释放未使用的已分配的块的过程叫做垃圾收集。...例如java,ML,Lisp之类的高级语言就依赖垃圾收集来释放已分配的块。
惰性分配的原因是确保大文件的mmap是快速的,并且比物理内存大的文件的mmap是可能的。 跟踪mmap为每个进程映射的内容。...实现mmap:在进程的地址空间中找到一个未使用的区域来映射文件,并将VMA添加到进程的映射区域表中。...添加代码以导致在mmap的区域中产生页面错误,从而分配一页物理内存,将4096字节的相关文件读入该页面,并将其映射到用户地址空间。...修改exit将进程的已映射区域取消映射,就像调用了munmap一样。运行mmaptest;mmap_test应该通过,但可能不会通过fork_test。...根据提示2、3、4,参考lazy实验中的分配方法(将当前p->sz作为分配的虚拟起始地址,但不实际分配物理页面),此函数写在sysfile.c中就可以使用静态函数argfd同时解析文件描述符和struct
.text: 已编译程序的机器代码。 .rodata: 只读数据。 .data: 已初始化的全局和静态变量。局部变量保存在栈上。...p设为1, 以防止程序引用到不存在的区域 4、M=1 为mmap映射区域分配;M=0为heap区域分配 5、 A=0 为主分配区分配;A=1 为非主分配区分配。...当用户的请求超过 mmap 分配阈值,并且主分配区使用 sbrk()分配失败的时候,或是非主分配区在 top chunk 中不能分配到需要的内存时,ptmalloc 会尝试使用 mmap()直接映射一块内存到进程内存空间...使用 mmap 系统调用为程序的内存空间映射一块 chunk_size align 4kB 大小的空间。然后将内存指针返回给用户。...如果free的是空指针,则返回,什么都不做。 判断当前chunk是否是mmap映射区域映射的内存,如果是,则直接munmap()释放这块内存。
(物理内存),因此完全可以分配远远大于物理内存大小的虚拟空间(例如 16G 内存主机分配 1000G 的 mmap 内存空间); mmap 负责映射文件逻辑上一段连续的数据(物理上可以不连续存储)映射为连续内存...vm_area_struct: linux使用vm_area_struct来表示一个独立的虚拟内存区域,一个进程可以使用多个vm_area_struct来表示不用类型的虚拟内存区域(如堆,栈,代码段,MMAP...),实现文件物理地址和进程虚拟地址的一一映射关系 为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体...效率差; 6.mmap使用细节 使用mmap需要注意的一个关键点是,mmap映射区域大小必须是物理页大小(page_size)的整倍数(32位系统中通常是4k字节)。...五、mmap映射 在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用
mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于 C 运行时库使用 mmap 映射区域和堆进行内存分配。...也就是说, allocator 不但要管理已分配的内存块, 还需要管理空闲的内存块, 当响应用户分配要求时, allocator 会首先在空闲空间中寻找一块合适的内存给用户, 在空闲空间中找不到的情况下才分配一块新的内存...p设为1, 以防止程序引用到不存在的区域 4、M=1 为mmap映射区域分配;M=0为heap区域分配 5、 A=0 为主分配区分配;A=1 为非主分配区分配。...当用户的请求超过 mmap 分配阈值,并且主分配区使用 sbrk()分配失败的时候,或是非主分配区在 top chunk 中不能分配到需要的内存时,ptmalloc 会尝试使用 mmap()直接映射一块内存到进程内存空间...9、使用 mmap 系统调用为程序的内存空间映射一块 chunk_size align 4kB 大小的空间。 然后将内存指针返回给用户。
linux内核使用vmareastruct结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vmareastruct结构来分别表示不同类型的虚拟内存区域...3、为此虚拟区分配一个vmareastruct结构,接着对这个结构的各个域进行了初始化 4、将新建的虚拟区结构(vmareastruct)插入进程的虚拟地址区域链表或树中 (二)调用内核空间的系统调用函数...而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。...mmap使用细节 1、使用mmap需要注意的一个关键点是,mmap映射区域大小必须是物理页大小(page_size)的整倍数(32位系统中通常是4k字节)。...② 直接使用堆外内存,如DirectByteBuffer:这种方式是直接在堆外分配一个内存(即,native memory)来存储数据,程序通过JNI直接将数据读/写到堆外内存中。
用户进程部分分段存储内容如下表所示(按地址递减顺序): 名称 存储内容 栈 局部变量、函数参数、返回地址等 堆 动态分配的内存 BSS段 未初始化或初值为0的全局变量和静态局部变量 数据段 已初始化且初值非...3 内存映射段(mmap) 此处,内核将硬盘文件的内容直接映射到内存, 任何应用程序都可通过Linux的mmap()系统调用或Windows的CreateFileMapping()/MapViewOfFile...使用堆时经常出现两种问题:1) 释放或改写仍在使用的内存(“内存破坏”);2)未释放不再使用的内存(“内存泄漏”)。当释放次数少于申请次数时,可能已造成内存泄漏。...此外,由于找到的堆结点大小不一定正好等于申请的大小,系统会自动将多余的部分重新放入空闲链表中。 ⑧碎片问题:栈不会存在碎片问题,因为栈是先进后出的队列,内存块弹出栈之前,在其上面的后进的栈内容已弹出。...某些编译器将未初始化的全局变量保存在common段,链接时再将其放入BSS段。在编译阶段可通过-fno-common选项来禁止将未初始化的全局变量放入common段。
所以虚拟页的三种状态的实际含义如下: 未分配虚拟页,指的是没有使用mmap建立vm_area_struct,所以也就没有对应到具体的页表项 已分配虚拟页,未映射到物理页,指的是已经使用了mmap建立的...vm_area_struct,可以映射到对应的页表项,但是页表项没有指向具体的物理页 已分配虚拟页,已映射到物理页,指的是已经使用了mmap建立的vm_area_struct,可以映射到对应的页表项,并且页表项指向具体的物理页...而mmap是C标准库提供给用户程序的一个函数来使用内存映射,建立起文件地址空间和虚拟内存区域的映射关系。...对文件内容的修改会被写回到后备文件。 后备文件的私有映射,多个进程的vm_area_struct指向同一个物理内存区域,采用写时拷贝的方式,当一个进程对文件内容做修改,不会被其他进程看到。...一般用在加载共享代码库 匿名文件的共享映射,内核创建一个初始都是0的物理内存区域,然后多个进程的vm_area_struct指向这个共享的物理内存区域,对该区域内容的修改对所有进程可见。
32位经典布局 在该内存布局示例图中,mmap 区域与栈区域相对增长,这意味着堆只有 1GB 的虚拟地址空间可以使用,继续增长就会进入 mmap 映射区域, 这显然不是我们想要的。...堆至底向上扩展,mmap 映射区域至顶向下扩展,mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于C运行时库使用 mmap 映射区域和堆进行内存分配。...❞ 3.2.2 MMap操作 在LINUX中我们可以使用mmap用来在进程虚拟内存地址空间中分配地址空间,创建和物理内存的映射关系。 共享内存 mmap()函数将一个文件或者其它对象映射进内存。...这里值得注意的是,mmap只是在虚拟内存分配了地址空间,只有在第一次访问虚拟内存的时候才分配物理内存。 在mmap之后,并没有在将文件内容加载到物理页上,只有在虚拟内存中分配了地址空间。...mmap 分配阈值设置为 64K,大于 64K 的内存分配都使用mmap 向系统分配,释放大于 64K 的内存将调用 munmap 释放回系统。
0m0.010s sys 0m0.638s 再此读取同一个文件,由于系统已经将读取过的文件内容放入了 Page Cache ,这次耗时大大缩短: # time cat /root/dd.out...另外,应用程序可以使用 mmap ,将文件内容映射到进程的虚拟地址空间,可以像读写内存一样直接读写硬盘上的文件。进程的虚拟内存直接和 Page Cache 映射。...为了了解内核是怎么管理 Page Cache 的,我们先看一下 VFS 的几个核心对象: file 存放已打开的文件信息,是进程访问文件的接口; dentry 使用 dentry 将文件组织成目录树结构...这颗树上挂的都是页偏移量对应的内存页,如果没找到,就说明文件内容还没加载进内存,就会分配内存页,将文件内容加载到内存中,然后把内存页挂在 xarray 树上。...由于 mmap 系统调用分配内存的效率比较低, malloc 会先使用 mmap 向操作系统申请一块比较大的内存,然后再通过各种优化手段让内存分配的效率最大化。
其中,Heap区是程序的动态内存区,同时也是C++内存泄漏的温床。malloc、free均发生在这个区域。本文将简单介绍下glibc在动态内存管理方面的机制,抛砖引玉,希望能和大家多多交流。...在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。...那么,既然brk、mmap提供了内存分配的功能,直接使用brk、mmap进行内存管理不是更简单吗,为什么需要glibc呢?...重点看下小块内存(size > DEFAULT_MMAP_THRESHOLD)的分配,glibc使用的内存池如下图示: 内存池 内存池保存在bins这个长128的数组中,每个元素都是一双向个链表。...此时,由于brk = 662k,而释放的内存是位于[512k, 552k]之间,无法通过移动brk指针,将区域内内存交还操作系统,因此,在[512k, 552k]的区域内便形成了一个内存空洞 ----
为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息...而使用 mmap 操作文件,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。...MAP_STACK (since Linux 2.6.27) 将映射分配到适合进程或线程的栈空间。该标志目前是无操作的,但在 glibc 线程实现中有使用。...可以通过调用 msync() 将映射区内容同步到磁盘文件。 扩缩映射:mremap 如果需要运行时动态扩缩映射区域大小,可以使用 mremap(2) 系统调用。...以下是一些常见的使用场景: 5.1 映射文件:减少数据拷贝,提高 IO 效率 将文件映射到进程的地址空间,使得进程可以通过直接读写内存来访问文件内容,而不必使用 read 和 write 等系统调用。
为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化。 将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中。...对这类虚拟内存段的读写会使操作系统去读写磁盘文件中与之对应的部分。 mmap 函数创建一个指向一段内存区域的指针,该内存区域与可以通过一个打开的文件描述符访问的文件的内容相关联。...*vm_next; // 把进程所有已分配的内存区链接起来 pgprot_t vm_page_prot; // 内存区的权限 ......,如果进程写操作改变了内容,并不会立即更新,而是一定时间后系统会自动会写脏数据到对应硬盘的地址空间 使用mmap来创建文件映射,由于只建立了进程地址空间VMA,并没有马上分配page cache和建立映射关系...匿名映射使用进程的虚拟内存空间,它和malloc(3)类似,实际上有些malloc实现会使用mmap匿名映射分配内存,不过匿名映射不是POSIX标准中规定的。
3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化。 4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中。...(二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系 5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符...使用mmap操作文件,在创建新的虚拟内存区域,建立文件磁盘地址与虚拟内存区域映射关系的过程中,没有任何文件拷贝操作。...在之后访问数据时发现内存中并无数据,从而发起的缺页异常,此时通过建立好的映射关系,使用 一次数据拷贝 ,就可以将磁盘中的数据传入内存中的用户空间,供进程使用。...注意:当映射关系解除后,对原来映射地址的继续访问,将导致段错误发生。 msync函数 将共享内存区的数据,与磁盘上文件内容立即同步。
linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域...为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化 将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中 (二)调用内核空间的系统调用函数...mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系 为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集...而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。...mmap使用细节 使用mmap需要注意的一个关键点是,mmap映射区域大小必须是物理页大小(page_size)的整倍数(32位系统中通常是4k字节)。
Linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域...3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化。 4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中。...使用mmap操作文件,在创建新的虚拟内存区域,建立文件磁盘地址与虚拟内存区域映射关系的过程中,没有任何文件拷贝操作。...在之后访问数据时发现内存中并无数据,从而发起的缺页异常,此时通过建立好的映射关系,使用 一次数据拷贝 ,就可以将磁盘中的数据传入内存中的用户空间,供进程使用。...注意:当映射关系解除后,对原来映射地址的继续访问,将导致段错误发生。 msync函数 将共享内存区的数据,与磁盘上文件内容立即同步。
通常代码区是共享的,其他执行程序可调用它 未初始化的数据段:存放未初始化的全局变量 已初始化的数据段:存放已初始化的全局变量,比如静态全局变量,静态局部变量和常量等 内存映射区域:例如将动态库,共享内存等虚拟空间的内存映射到物理空间的内存..., len); 使用mmap的目的是将内核中读缓冲区的地址与用户空间的缓冲区进行映射,从而实现内核缓冲区与应用陈鼓内存的共享,省去了将数据从内核读缓冲区拷贝到用户缓冲区的过程,内核读缓冲区仍需将数据到内核写缓冲区...malloc()分配出来的用户态内存 堆外内存(DirectBuffer)在使用后需要应用程序手动回收,而堆内存(HeapBuffer)的数据在GC时可能会被自动回收。...,不能使用 NIO 方式 版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。...如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用.但需注意,直接对该段内存写时不会写入超过当前文件大小的内容....实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。...不执行预读,只为已存在于内存中的页面建立页表入口。 fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。 offset:被映射对象内容的起点。 2....除此之外,堆栈使用的空间也属于基本要求,所以也是在建立进程时就分配好的,如图3.1所示: 图3.1 进程虚拟空间的划分 在内核中,这样每个区域用一个结构struct vm_area_struct 来表示...(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。
2.mmap系统调用 mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。...当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用.但需注意,直接对该段内存写时不会写入超过当前文件大小的内容....实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。...不执行预读,只为已存在于内存中的页面建立页表入口。 fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。 offset:被映射对象内容的起点。...除此之外,堆栈使用的空间也属于基本要求,所以也是在建立进程时就分配好的,如图3.1所示: 图3.1 进程虚拟空间的划分 在内核中,这样每个区域用一个结构struct vm_area_struct
当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用.但需注意,直接对该段内存写时不会写入超过当前文件大小的内容....实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。...不执行预读,只为已存在于内存中的页面建立页表入口。 fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。 offset:被映射对象内容的起点。 2....除此之外,堆栈使用的空间也属于基本要求,所以也是在建立进程时就分配好的,如图3.1所示: 图3.1 进程虚拟空间的划分 在内核中,这样每个区域用一个结构struct vm_area_struct...(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。
领取专属 10元无门槛券
手把手带您无忧上云