-
分页池内存持续增长_C++内存管理内存池1
2020-11-28 11:35:01内存池定义:是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。内存池作用:1.减少内存...内存池定义:
是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。
内存池作用:
1.减少内存碎片,每次申请一大片空间,当然减少了内存碎片
2.提高性能,其实malloc本身设计也是一个内存池,所以效率并不低。但是malloc会因为频繁的申请释放内存,而形成内存碎片,当我们申请内存有时很大,有时很小时。会使得程序内存占用提高,并且每次malloc都会额外的分配至少8个字节的内存来标识这个内存。也会造成内存增加,使得程序分页增多,内存多了,高速缓存命中就低,所以会使程序变慢。(以上只是个人猜测,为什么会影响性能,没有实际测试验证)
3.减少内存耗用,少了malloc额外申请内存。
下面是一个最简单的内存池设计。
大概是这个样子:
class Screen { public: Screen(int x):i(x){} int get() { return i; } //重载 operator new 当new的时候会调用该函数申请内存 void* operator new(size_t); //重载operator delete 当delete的时候会调用该函数释放内存 void operator delete(void*, size_t); private: //下一个对象的指针 Screen* next; //可用内存 static Screen* freeStore; //每次申请个数 static const int screenChunk; //类的数据 int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 5; void* Screen::operator new(size_t size){ Screen *p; if (!freeStore) { //list为空时,申请screenChunk个内存 size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast(new char[chunk]); //赋值next,形成一条链表 for (; p != &freeStore[screenChunk - 1]; ++p) p->next = p + 1; p->next = 0; } //获取头指针的内存 p = freeStore; //空指针链表头移位 freeStore = freeStore->next; return p; } void Screen::operator delete(void*p, size_t){ //将析构的内存放回可用内存池中 (static_cast(p))->next = freeStore; freeStore = static_cast(p); }//测试案例 void pool_1_test(){ cout << sizeof(Screen) << endl; size_t const N = 100; Screen* p[N]; //申请100个 for (int i = 0; i < N; ++i) { p[i] = new Screen(i); } //打印前十个 for (int i = 0; i < 10; ++i) { cout << p[i] << endl; } //释放内存 for (int i = 0; i < N; ++i) { delete p[i]; } }
执行结果:
16000001DF67A49140000001DF67A49150000001DF67A49160000001DF67A49170000001DF67A49180000001DF67A44AE0000001DF67A44AF0000001DF67A44B00000001DF67A44B10000001DF67A44B20
因为Screen大小为16个字节,并且我们每次申请5个内存,所以前5个内存是相邻的,并且间隔为16,而第5和第6个分别属于不同的内存申请区块,所以中间内存间隔远大于16。
总结:
该案例取自C++primer一书中的案例,内存池的设计已经非常的成熟,C++标准库里面还有大概有6种内存池设计,希望站在巨人的肩膀上,借鉴(学习)前人的智慧,来学习C++内存管理。
-
分页池内存持续增长_使用M1 Mac,内存已不再是以前的样子
2020-12-04 18:01:06是的,当Mac上的物理内存用完时,它将页面中的内容分页到磁盘上-甚至超高速SSD都比主内存慢!尽管速度差异远小于使用慢速旋转磁盘驱动器时的速度差异。 是什么原因导致Mac的物理内存不足?如果您一次打开了很多应用...终于有了苹果设计的处理器支持的第一批Mac。从外部来看,它们几乎取代了要更换的基于Intel的Mac。
但在内部,它们不像其他计算机。通过多年的iPhone和iPad迭代学习,Apple首次将其系统设计方法引入了Mac。
我们中那些习惯于以某些术语思考个人计算机的人将需要适应这一新现实。在这个世界里,苹果公司出售了三种不同的Mac机型,甚至没有公开内部处理器的时钟速度。(毕竟,它不适用于iPhone或iPad。)
但是,规格表上可能需要从旧的思维方式中最大转移的项目是系统内存。这项功能已经经常被人们误解(并经常与存储容量相混淆),现在带有苹果芯片的Mac正在以完全不同的方式使用它。
RAM的旧思维方式已经过去。欢迎来到统一内存体系结构的世界。
包装的一部分
像具有集成显卡的英特尔芯片一样,M1芯片也包含图形处理器,并且处理器核心和图形核心都共享系统内存。(而且,在M1的情况下,也是构成神经引擎的核心。)但是,在改变其术语以描述统一的内存体系结构时,Apple试图指出M1的方法有些不同。
最大的区别在于,在M1中,内存是M1体系结构本身的一部分。M1 Mac的主板上没有内存插槽,也没有永久性焊接内存芯片的区域。而是将内存集成到包含M1本身的同一包中。
这意味着当您购买基于M1的Mac并选择内存配置时,就是这样。还有许多其他的Mac,它们的焊接内存无法升级,但这有所不同,因为内存基本上是M1软件包本身的一部分。
从第一轮M1 Mac来看,M1似乎只能使用8GB或16GB的内存。这实际上可能不是硬性限制-也许苹果公司为了限制这些低端系统而坚持了下来。但是,除非苹果提供更高端的M1版本,否则我们不太可能看到Mac运行超过16GB的苹果芯片。
统一的好处
但是苹果并不是故意将内存集成到其片上系统中。之所以这样做,是因为它可以带来巨大的速度优势。
M1处理器的内存是单个池,处理器的任何部分都可以访问。如果系统需要更多的图形内存,则可以分配该内存。同样,如果它需要更多的神经引擎内存。更好的是,因为处理器的所有方面都可以访问所有系统内存,所以当图形内核需要访问以前由处理器内核访问的内容时,性能不会受到影响。在其他系统上,必须将数据从内存的一部分复制到另一部分,但是在M1上,可以立即访问。
从它们的方式来看,这些新的Mac有点像外星人。个人计算机的传统是一切都是模块化的,这是早期PC时代的产物。尽管Mac从未参与过PC的构建,但Apple用来组装Mac的零件却来自该行业。与智能手机相比,苹果一直在将智能手机的更多部分集成到其单处理器软件包中,以提高效率。这些新的Mac更像是智能手机,而不是传统的PC。
你需要它吗?
关于第一轮M1 Mac,我所看到的最大批评之一就是它们没有提供足够的内存,最大可达到16GB。请记住,这些是性能最低的Mac机型,未来的机型可能会提供更多RAM选项。
但是,值得一提的是,当您戳一下它时,对多出更多内存的需求可能会有多松散。当然,很多人认为他们需要它-真的吗?
是的,当Mac上的物理内存用完时,它将页面中的内容分页到磁盘上-甚至超高速SSD都比主内存慢!尽管速度差异远小于使用慢速旋转磁盘驱动器时的速度差异。
是什么原因导致Mac的物理内存不足?如果您一次打开了很多应用程序,或者浏览器打开了数百个选项卡,或者您使用的应用程序将非常大的文件(例如Photoshop文件)加载到内存中。如果您经常执行此操作,则可能需要更多的内存...。但是,如果您经常执行此操作,则可能不想立即购买一台M1 Mac。无疑将在明年推出中端和高端机型,这些机型无疑将提供更多的RAM选项和更多的处理器功能。
但是,如果您将统一内存架构的效率与SSD存储的速度结合在一起,并考虑大多数日常使用案例,那么我很确定大多数普通用户都可以使用8GB统一内存,或者,如果您愿意绝对可以,将其升级到16GB。(我做到了。)
下一步是什么?
无法想象2021年会过去,而没有苹果推出具有更强大处理器和更多内存选项的新Mac。至少高端MacBook Pro和iMac可以使用更新,这些更新提供了基本M1以外的某些选项。
从长远来看,苹果是否有可能使用带有自己专用内存的外部图形处理器来构建系统?这似乎是不可避免的,至少在高端市场是这样—如果您不能在其中插入可笑的图形卡,那么Mac Pro是什么呢?
但是,苹果也很可能会在扩大处理器规模时继续扩大内存选项,在增加内核时增加内存,而这些芯片可能是大多数Mac型号中提供的芯片。
M1中的统一内存架构是这些Mac如此之快的原因之一-但所有Mac用户都将不得不放弃我们对计算机如何工作以及如何配置的一些假设。而且,如果您真的舍不得购买只有16GB RAM的Mac,请不要生气-耐心等待。更多的Apple Mac正在生产中。
-
分页池内存持续增长_鸿蒙内核源码分析(从进程/线程视角看内存)
2020-11-23 03:37:56这篇文章说说内存,内存的管理是极其复杂的模块,涉及到非常多概念,光地址就有逻辑,线性,物理地址三个,网上文章很多,参差不齐,没有很好基础或实战经验的同学基本得懵掉,本篇最后也有这些概念介绍。...这篇文章说说内存,内存的管理是极其复杂的模块,涉及到非常多概念,光地址就有逻辑,线性,物理地址三个,网上文章很多,参差不齐,没有很好基础或实战经验的同学基本得懵掉,本篇最后也有这些概念介绍。系列篇打算用三篇来讲述鸿蒙内核的内存管理机制。由浅入深,层层递进。我们换个视角切入,将从进程和线程创建的视角看内存的运作机制。为何从进程和线程角度?
两个原因:1.内存就是给他们使用的,只是分了内核空间和用户空间。用户空间的进程分配用到了虚拟内存,线程(task)需要分配栈空间 2.系列文章对进程和线程的管理和调度已经说完了,但是没有说内存,还有IPC(也是复杂的模块),有了前面的基础我们再来说鸿蒙的内存管理会轻松些。没有看过前面文章的建议去翻一下鸿蒙源码分析(总目录),特别是张大爷的故事,再来看本篇。
进程内存描述符LosVmSpace
typedef struct ProcessCB { //..只留下相关部分 LosVmSpace *vmSpace; /**< VMM space for processes */}LosProcessCB;typedef struct VmSpace { LOS_DL_LIST node; /**< vm space dl list */ LOS_DL_LIST regions; /**< region dl list */ LosRbTree regionRbTree; /**< region red-black tree root */ LosMux regionMux; /**< region list mutex lock */ VADDR_T base; /**< vm space base addr */ UINT32 size; /**< vm space size */ VADDR_T heapBase; /**< vm space heap base address */ VADDR_T heapNow; /**< vm space heap base now */ LosVmMapRegion *heap; /**< heap region */ VADDR_T mapBase; /**< vm space mapping area base */ UINT32 mapSize; /**< vm space mapping area size */ LosArchMmu archMmu; /**< vm mapping physical memory */#ifdef LOSCFG_DRIVERS_TZDRIVER VADDR_T codeStart; /**< user process code area start */ VADDR_T codeEnd; /**< user process code area end */#endif} LosVmSpace;
被进程使用的内存叫进程内存描述符LosVmSpace(也叫虚拟内存空间),虚拟内存空间有多个虚拟存储区域(region),Linux内核中对这些虚拟存储区域的组织方式有两种,一种是采用双循环链表(regions),还有一种是采用树的结构。Linux内核从2.4.10开始,Linux内核对虚拟区的组织不再采用一般平衡二叉树,而是采用红黑树(regionRbTree),这是出于效率的考虑,就是增删改查更快了。node会加入到全局的g_vmSpaceList链表中,曾有人私信笔者LOS_DL_LIST里面只有两个指针数据去哪了?答案是:谁用它谁就是数据。 链表把所有进程拉进大循环,还记得鸿蒙内核进程池的大小吗?默认64个,另外就是堆栈空间等信息。这里大概说这么多,后续还会拆开细讲。
从用户态的第一个进程初始化说起
所有应用程序的爸爸都是"init"这个用户进程fork来的,看代码他是如何初始化的,这个函数之前也讲过,但没有说内存部分,这次专讲内存部分,涉及代码都已经加了注释。用户进程就是只能运行在用户空间,内核空间是通过系统调用访问的。
LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID){ INT32 ret; UINT32 size; TSK_INIT_PARAM_S param = { 0 }; VOID *stack = NULL; VOID *userText = NULL; CHAR *userInitTextStart = (CHAR *)&__user_init_entry;//*kfy 代码区开始位置 CHAR *userInitBssStart = (CHAR *)&__user_init_bss;//*kyf 未初始化数据区(BSS)。在运行时改变其值 CHAR *userInitEnd = (CHAR *)&__user_init_end;//*kyf 结束地址 UINT32 initBssSize = userInitEnd - userInitBssStart; UINT32 initSize = userInitEnd - userInitTextStart; LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess); ret = OsProcessCreateInit(processCB, OS_USER_MODE, "Init", OS_PROCESS_USERINIT_PRIORITY);//*kyf 初始化用户进程,它将是所有应用程序的父进程 if (ret != LOS_OK) { return ret; } userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);//*kyf 分配连续的物理页 if (userText == NULL) { ret = LOS_NOK; goto ERROR; } (VOID)memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize);//*kyf 安全copy __user_init_load_addr -> userText ret = LOS_VaddrToPaddrMmap(processCB->vmSpace, (VADDR_T)(UINTPTR)userInitTextStart, LOS_PaddrQuery(userText), initSize, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE | VM_MAP_REGION_FLAG_PERM_EXECUTE | VM_MAP_REGION_FLAG_PERM_USER);//*kyf 虚拟地址与物理地址的映射 if (ret < 0) { goto ERROR; } (VOID)memset_s((VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart), initBssSize, 0, initBssSize);//*kyf 除了代码段,其余都清0 stack = OsUserInitStackAlloc(g_userInitProcess, &size);//*kyf 初始化堆栈区 if (stack == NULL) { PRINTK("user init process malloc user stack failed!"); ret = LOS_NOK; goto ERROR; } param.pfnTaskEntry = (TSK_ENTRY_FUNC)userInitTextStart;//*kyf 从代码区开始执行,也就是应用程序main 函数的位置 param.userParam.userSP = (UINTPTR)stack + size;//*kyf 指向栈顶 param.userParam.userMapBase = (UINTPTR)stack;//*kyf 栈底 param.userParam.userMapSize = size;//*kyf 栈大小 param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;//*kyf 可结合的(joinable)能够被其他线程收回其资源和杀死 ret = OsUserInitProcessStart(g_userInitProcess, ¶m);//*kyf 创建一个任务,来运行main函数 if (ret != LOS_OK) { (VOID)OsUnMMap(processCB->vmSpace, param.userParam.userMapBase, param.userParam.userMapSize); goto ERROR; } return LOS_OK;ERROR: (VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT); OsDeInitPCB(processCB); return ret;}
鸿蒙现有开源终端设备支持的128KB-128MB内存 ,这三个值需要外部提供,先指定空间大小,内核才能还是管理。
CHAR *userInitTextStart = (CHAR *)&__user_init_entry;//*kfy 代码区开始位置 CHAR *userInitBssStart = (CHAR *)&__user_init_bss;//*kyf 未初始化数据区(BSS)。在运行时改变其值 CHAR *userInitEnd = (CHAR *)&__user_init_end;//*kyf 结束地址
代码区:应用程序源码码经过编译后,通过加载器加载到这里,第一条指令就是 main()
BSS:英文全称叫Block Started by Symbol ,未初始化的全局变量和静态变量的内存空间,并且清0
LOS_PhysPagesAllocContiguous: 从物理页分配连续的地址空间。
LOS_VaddrToPaddrMmap:将虚拟内存映射成物理地址。 这两个函数先不展开,放在下篇说,需要了解物理地址页管理部分。
同时通过g_userInitProcess创建了进程的第一个task,回调函数指向了代码区userInitTextStart,也就是像java等上层应用开发的main函数所在位置,等该任务被调度后,CPU的PC寄存器值将会被改成userInitTextStart,程序从这开始执行。明白了吗?
再来,main任务的栈内存是怎么来的?
STATIC VOID *OsUserInitStackAlloc(UINT32 processID, UINT32 *size){ LosVmMapRegion *region = NULL; LosProcessCB *processCB = OS_PCB_FROM_PID(processID); UINT32 stackSize = ALIGN(OS_USER_TASK_STACK_SIZE, PAGE_SIZE);//*kyf region = LOS_RegionAlloc(processCB->vmSpace, 0, stackSize, VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE, 0); if (region == NULL) { return NULL; } LOS_SetRegionTypeAnon(region); region->regionFlags |= VM_MAP_REGION_FLAG_STACK; *size = stackSize; return (VOID *)(UINTPTR)region->range.base;}struct VmMapRegion { LosRbNode rbNode; /**< region red-black tree node */ LosVmSpace *space; LOS_DL_LIST node; /**< region dl list */ LosVmMapRange range; /**< region address range */ VM_OFFSET_T pgOff; /**< region page offset to file */ UINT32 regionFlags; /**< region flags: cow, user_wired */ UINT32 shmid; /**< shmid about shared region */ UINT8 protectFlags; /**< vm region protect flags: PROT_READ, PROT_WRITE, */ UINT8 forkFlags; /**< vm space fork flags: COPY, ZERO, */ UINT8 regionType; /**< vm region type: ANON, FILE, DEV */ union { struct VmRegionFile { unsigned int fileMagic; struct file *file; const LosVmFileOps *vmFOps; } rf; struct VmRegionAnon { LOS_DL_LIST node; /**< region LosVmPage list */ } ra; struct VmRegionDev { LOS_DL_LIST node; /**< region LosVmPage list */ const LosVmFileOps *vmFOps; } rd; } unTypeData;};
VmMapRegion(线性区描述符),该结构体描述了 protectFlags(权限),LosVmMapRange(范围),线性区的类型(regionType)。映射类型(unTypeData):按文件映射,匿名映射,特殊设备映射,这是个联合体,具体下篇再展开讲。
一些概念
一、逻辑地址(有时也称虚拟地址)
逻辑地址(Logical Address) 是指由程序产生的与段相关的偏移地址部分。例如在C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于当前进程数据段的地址,和绝对物理地址无关。
只有在Intel处理器的实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,CPU不进行自动地址转换)。逻辑地址也就是在Intel 处理器的保护模式下,程序执行代码段限长内的偏移地址(假定代码段、数据段完全一样)。
CPU启动保护模式后,程序运行在虚拟地址空间中。注意,并不是所有的“程序”都是运行在虚拟地址中。CPU在启动的时候是运行在实模式的,Bootloader以及内核在初始化页表之前并不使用虚拟地址,而是直接使用物理地址的。
应用程序仅需与逻辑地址打交道,而分段和分页机制仅系统编程涉及,应用程序虽然可以直接操作内存,但是也只能在操作系统分配的内存段中操作。二、线性地址
线性地址(Linear Address)是逻辑地址到物理地址转换的中间层。程序代码经编译后会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。
若启用了分页机制,则线性地址会再此转换产生一个物理地址。若没有启用分页机制,则线性地址就是物理地址。三、物理地址
物理地址(Physical Address)是指出现在CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终地址。若启用了分页机制,则线性地址会使用页目录和页表中的项转换为物理地址。若没有启用分页机制,则线性地址直接就是物理地址。四、虚拟内存
虚拟内存(Virtual Memory)是指计算机呈现出比实际拥有的内存大得多的内存量。因此它允许程序员编写并运行比实际系统拥有的内存大得多的程序。这使得许多大型项目也能够在具有有限内存资源的系统上实现。原文链接:https://my.oschina.net/u/3751245/blog/4646802?_from=gitee_search
感觉本文对你有帮助,关注我一起学习进步
-
分页池内存持续增长_Java技术学习之对虚拟机(JVM)内存模型的分析
2020-11-29 11:38:45JVM会将Java进程所管理的内存划分为若干不同的数据区域,这些区域有各自的用途、创建/销毁时间。JVM内存数据:栈管运行,堆管存储第一章 线程私有区域 线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束...JVM会将Java进程所管理的内存划分为若干不同的数据区域,这些区域有各自的用途、创建/销毁时间。
JVM内存数据:栈管运行,堆管存储
第一章 线程私有区域
线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束而创建/销毁(在Hotspot VM内, 每个线程都与操作系统的本地线程直接映射, 因此这部分内存区域的存/否跟随本地线程的生/死),下面来详细的看看java技术。
1.1 Native Method Stack本地方法栈
是在Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
1.2 PC Register程序计数器
每个线程都有一个程序计算器,就是一个指针,指向方法区中的方法字节码(下一个将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
作用是当前线程所执行字节码的行号指示器(类似于传统CPU模型中的PC), PC在每次指令执行后自增, 维护下一个将要执行指令的地址. 在JVM模型中, 字节码解释器就是通过改变PC值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖PC完成(仅限于Java方法, Native方法该计数器值为undefined)。
不同于OS以进程为单位调度, JVM中的并发是通过线程切换并分配时间片执行来实现的. 在任何一个时刻, 一个处理器内核只会执行一条线程中的指令. 因此, 为了线程切换后能恢复到正确的执行位置, 每条线程都需要有一个独立的程序计数器, 这类内存被称为“线程私有”内存.
1.3 Java Stack(虚拟机栈)
栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致,是线程私有的。
基本类型的变量和对象的引用变量都是在函数的栈内存中分配。
栈帧中主要保存3类数据:
本地变量(Local Variables):输入参数和输出参数以及方法内的变量;
栈操作(Operand Stack):记录出栈、入栈的操作;
栈帧数据(Frame Data):包括类文件、方法等等。
栈运行原理
栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,B方法又调用了C方法,于是产生栈帧F3也被压入栈…… 依次执行完毕后,先弹出后进......F3栈帧,再弹出F2栈帧,再弹出F1栈帧。
遵循“先进后出”/“后进先出”原则。
第二章 线程共享区域
随虚拟机的启动/关闭而创建/销毁
2.1 Method Area方法区
常说的永久代(Permanent Generation), 用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样HotSpot的垃圾收集器就可以像管理Java堆一样管理这部分内存, 而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。
1.7和1.8的异同
不过在1.7的HotSpot已经将原本放在永久代的字符串常量池移出
而在1.8中, 永久区已经被彻底移除, 取而代之的是元数据区Metaspace(这一点在查看GC日志和使用jstat -gcutil查看GC情况时可以观察到),与永久代不同, 如果不指定Metaspace大小, 如果方法区持续增长, VM会默认耗尽所有系统内存.
运行时常量池
方法区的一部分. Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项常量池(Constant Pool Table)用于存放编译期生成的各种字面量和符号引用, 这部分内容会存放到方法区的运行时常量池中(如前面从test方法中读到的signature信息). 但Java语言并不要求常量一定只能在编译期产生, 即并非预置入Class文件中常量池的内容才能进入方法区运行时常量 池, 运行期间也可能将新的常量放入池中, 如String的intern()方法.
2.2 Heap(Java堆)
堆这块区域是JVM中最大的,应用的对象和数据都是存在这个区域,这块区域也是线程共享的,也是 gc 主要的回收区,一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:
新生区
新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被new出来的。幸存区有两个:0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园进行垃圾回收(Minor GC),将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区。那如果1去也满了呢?再移动到养老区。若养老区也满了,那么这个时候将产生Major GC(FullGCC),进行养老区的内存清理。若养老区执行Full GC 之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”。
如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:
a.Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
b.代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。
养老区
养老区用于保存从新生区筛选出来的 JAVA 对象,一般池对象都在这个区域活跃。
永久区
永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。
如果出现java.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。原因有二:
a. 程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。
b. 大量动态反射生成的类不断被加载,最终导致Perm区被占满。
说明:
Jdk1.6及之前:常量池分配在永久代 。
Jdk1.7:有,但已经逐步“去永久代” 。
Jdk1.8及之后:无(java.lang.OutOfMemoryError: PermGen space,这种错误将不会出现在JDK1.8中)。
说明:方法区和堆内存的异议:
实际而言,方法区和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)”,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区的一个实现,jdk1.7的版本中,已经将原本放在永久代的字符串常量池移走。
重庆千锋Java培训作为中国IT研发人才一体化服务的开拓者,为学生制定合理有序的学习计划,0学费入学,2周免费试听不满意不收费,与学员签订就业协议,坚持良心面授,从千锋重庆Java培训班出去的学员均已高薪就业。重庆千锋推出的免费Java视频教程,让学员能够方面的巩固基础技术能力。了解更多的千锋重庆java动态,不妨来实地考察一下。
-
分页池内存持续增长_gRPC双向流服务(go语言实现)内存优化小记
2020-11-29 20:14:22三、GC机制 1、Go语言有内存池,每次GC后只是把没有使用的对象放入内存池中,不会立即归还给操作系统。这样子如果程序还需要内存的话会优先从内存池中分配,不会向操作系统请求分配内存。内存池中对象如果5分钟还... -
分页池内存持续增长_记一次害阿里程序员差点被开除的线程池故障
2020-12-06 18:43:49目前的重新统计接口是单线程的,粗略算了算一共有100万条订单信息,每100条的处理大约是10秒,所以理论上处理完全部信息需要28个小时,这还不算因为 mysql 中 limit 分页导致的后期查询时间以及可能出现的内存溢出... -
分页缓冲池内存占用过高
2017-10-25 09:47:55发现还是很高,最后百度得知是分页缓冲池占用过高,一看果然,分页缓冲池5g了,然后继续百度,关闭虚拟内存,但分页缓冲池还是很高,不知是否有高手遇到类似问题,该如何解决呢 ![图片说明]... -
win10分页缓冲池内存占用过高
2020-03-17 17:45:04win10分页缓冲池内存占用过高 2020年3月14左右,在使用电脑过程中发现内存占用不断上涨的情况,查看内存发现,分页缓冲池占用的内存快速增加,百度后了解到,可能是内存泄漏,不确定。 解决办法: 在我的电脑上经过 ... -
分页池和非分页池
2018-06-16 10:15:52windows把虚拟地址:用户空间空间:应用程序使用 系统地址空间:分页池:映射到分页文件的虚拟地址当要使用时才交换到物理内存中的系统调度 非分页池:页面一直锁定在物理内存中,不会被换出到页面交换文件中页面... -
分页池和非分页池的基本概念
2017-09-11 10:09:52系统地址空间分为分页池和非分页池,分页池是指映射到分页文件的虚拟地址,当要使用该地址时才交换到物理内存中,由系统来调度;非分页池是指直接在物理内存中分配的内存。“页面缓冲池”就是进程占用的分页 -
基本概念:分页池和非分页池
2019-10-07 02:10:25在网上找搜索概念的解释,描述的不是很清晰。摘录两个比较靠谱的: ...系统地址空间分为分页池和非分页池,分页池是指映射到分页文件的虚拟地址,当要使用该地址时才交换到物理内存中,由系统... -
Nonpaged Pool(未分页池)占用内存过多分析定位
2018-09-26 14:34:11Nonpaged Pool(未分页池)占用内存过多分析定位 问题定位用到三个小工具:RAMMap、Poolmon、Strings.exe 背景:接到项目反馈,应用服务器内存占用达到80%以上,w3wp占400M,剩余的11G内存跑哪里去了呢,任务管理器... -
分页缓冲池内存过高_深度剖析 Kafka Producer 的缓冲池机制
2020-12-04 09:21:56上次跟大家分享的文章「Kafka Producer 异步发送...在新版的 Kafka Producer 中,设计了一个消息缓冲池,在创建 Producer 时会默认创建一个大小为 32M 的缓冲池,也可以通过 buffer.memory 参数指定缓冲池的大小,... -
关于 非分页缓冲池 内存占用过高但任务管理器无程序 的特殊原因
2020-02-29 18:18:06本机发生条件: 使用百度网盘下载时,非分页缓冲池占用 以肉眼可见的速度增长,游戏等其他操作无影响。...可能是限速策略和网卡驱动出现了冲突,导致驱动内存泄漏,使非分页缓冲池内存爆炸。 Tips:... -
分页内存和非分页内存区别
2017-03-20 22:31:45在写驱动的时候,经常要调用ExAllocatePoolWithTag函数分配内存,其中第一个参数可以是如下几个: ...从非分页内存池中分配内存 ...从非分页内存池中分配内存,如果不能分配则产生bugchec -
分页缓冲池内存过高_关于mysql数据库监控指标总结--缓冲池使用情况
2020-12-14 18:16:04缓冲池使用情况MySQL 默认的存储引擎 InnoDB 使用了一片称为缓冲池的内存区域,用于缓存数据表与索引的数据。缓冲池指标属于资源指标,而非工作指标。默认设置下,缓冲池的大小通常相对较小,为 128... -
分页内存和非分页内存
2011-11-07 10:35:36在写驱动的时候,经常要调用...从非分页内存池中分配内存 PagedPool 从分页内存池中分配内存 NonPagedPoolMustSucceed 从非分页内存池中分配内存,如果不能分配则产生bugcheck NonPagedP -
分页缓冲池内存过高_MySql那些事儿(二):InnoDB架构介绍之内存篇
2020-11-22 07:58:50更多特点请参考阅读最下方:Benefits of Using InnoDB Tables ###InnoDB内存结构 接下来我们来看一张InnoDB的逻辑架构图: Buffer Pool InnoDB使用malloc()操作在启动时为整个缓冲池分配缓存,缓冲池存储着常访问的... -
推动Windows的限制:分页和非分页池
2018-01-31 21:27:47这一次我将描述两个基本的内核资源,分页池和非分页池,它们是基于这些资源的,并且直接负责包括最大进程数,同步对象和句柄在内的许多其他系统资源限制。这是整个推动限制系列的索引。虽然他们可以独立存在,但他们... -
分页与非分页内存
2010-07-30 18:42:00在写驱动的时候,经常要调用ExAllocatePoolWithTag函数分配内存,其中第一个参数可以是如下几个: ...NonPagedPoolCacheAligned 从非分页内存池中分配内存,并确保内存与CPU cache对齐 NonPagedPoolCacheAligne -
iis6.0站点打不开,找不到服务器或 DNS 错误。非分页缓冲池内存不足故障一例...
2019-10-02 20:30:45非分页缓冲池内存不足故障一例 (2011-09-09 15:04:37)iis6.0站点打不开,找不到服务器或 DNS 错误。 问题: iis装好后,运行了一段时间;一个月后有一天打开网站,却显示该页无法访问,telnet 端口开始可以,但... -
分页缓冲池占用很高怎么解决_王道操作系统学习笔记(六)分页内存管理
2020-12-06 14:03:36分页存储管理/非连续分配管理方式的由来连续分配的方式:...(这个区域长度是不固定的)分页存储:如果允许讲进程分散地按固定大小的装入道各个不相邻的分区中,便可以充分利用内存。包括了,基本分页存储管理,基... -
非分页缓冲池内存不足 Httperr.log 文件中记录消息和"Connections_refused" 网站不能打开
2012-09-19 10:42:01非分页缓冲池内存不足 Httperr.log 文件中记录消息和"Connections_refused" 问题描述: server2003的iis 运行一段时间后莫名其妙的就不能打开了?然后重启iis都不行,然后重新注册iis以及重启服务器就恢复了 解决...
-
岳阳职业技术学院《电力电子技术》试卷库(多套试卷且含答案).pdf
-
在Windows上编译FreeRDP
-
西南科技大学《高等数学B2》期末考试试卷(含答案).pdf
-
【图像识别】基于LBP+LPQ算法融合人脸表情识别matlab源码
-
MySQL 触发器
-
[GYCTF2020]Blacklist
-
牛牛量化策略交易
-
[MRCTF2020]你传你呢
-
西南科技大学《自动控制原理》试题库(含答案).pdf
-
精通编译Makefile,Nina, 从底层uboot到Android
-
剑指19 - 顺时针打印矩阵
-
FFmpeg4.3系列之16:WebRTC之小白入门与视频聊天的实战
-
VMware vSphere ESXi 7 精讲/VCSA/VSAN
-
MySQL 高可用工具 heartbeat 实战部署详解
-
爬梯:MongoDB入门到高级到SpringBoot-API
-
西南科技大学《电路分析A》期末考试试卷(含答案).pdf
-
华为1+X——网络系统建设与运维(中级)
-
CS420课程总结class1-2
-
NFS 实现高可用(DRBD + heartbeat)
-
libFuzzer视频教程