在上期,小E通过学习方老师推荐的关于操作系统、虚拟化和Intel x86体系结构的经典著作,知道了90年代土豪的计算机长什么样,也知道了为什么不建议通过从硬盘“借”内存的方式,为虚拟机超分配内存。
那么,有没有其他的办法超分配内存呢?
小E边走边想,在路边买了一包葵花子,回到了知春路49号,刚打开包装,就被拉进了电话会议。等小E结束会议,发现自己的葵花子被吃得只剩一小半了,旁边另一个妹子小F桌上堆了一堆壳。
小F指着一堆葵花子壳,嬉皮笑脸地对小E说:“亲爱的,我看你的葵花子暂时不吃,就先替你吃了,吃完再还你还不行嘛!”
小E突然呆住了,小F还以为小E生气了,拉着小F的手说:“亲别生气嘛,上次我借你姨妈巾,后来不也还了你嘛,而且是还新的,不是把用过的还你的~”
小E站起来,一边跑向方老师座位,一边回头对小F说:“我没生气~等我回来请你去吃辣子鸡~”
小E一路跑到了方老师的座位,喊道:“啊啊啊,我知道啦~”
方老师刚给一个还不错的人面试完,问:“你知道什么了?”
“我,想,”小E说,“能不能让一个虚拟机,向另一个虚拟机借用暂时用不着的内存,用完再还?”
方老师给小E看了一眼自己的面试题库中的一道题:请简述内存气球技术的工作原理。
小E跑回座位开始查资料…
所谓内存气球,指的是虚拟化平台Hypervisor在虚拟机上运行一个内存气球进程。当其他虚拟机需要借用内存的时候,Hypervisor会操作这个进程,向内存使用量较低的虚拟机GuestOS申请内存,并把申请到的内存地址告诉Hypervisor。这样,Hypervisor就可以把这些内存给其他虚拟机使用了。
如图,VM2的内存是超分配的,而VM1体内有空闲的内存。当VM2的应用向GuestOS申请较多内存时,QEMU感知到VM2的内存使用率吃紧,会控制VM1体内的内存气球向VM1的GuestOS申请内存,得到的是GVA(Guest Virtual Address),并将其转换为GPA(Guest Physical Address)后,报告给Hypervisor。Hypervisor会将GPA再转换为HPA(Host Physical Address),并将其映射成为VM2的GPA,可以被VM2的GuestOS所使用。这个过程就叫做内存全球的膨胀。
那么,当VM2内存使用量降低的时候,VM1体内的内存气球会向guestos释放内存,这个过程就叫做内存气球的收缩。如果VM1本身的应用申请了较多内存导致VM1的内存水位到达阈值,Hypervisor也会让VM1体内的内存气球收缩,同时让其他内存利用率较低的VM通过内存气球,将内存借给VM2使用。
小E看完,觉得通往新世界的大门打开了……
这时,小F来了,拿着新买的一袋葵花子,拍了拍小E说:“谁这么好看”
小E一愣,突然想起来一个问题,拉着小F讨论:
如果在KVM中,开启了内存气球来实现内存的超分配,如实际上宿主机上只有320GB内存,却分配了400GB给虚拟机使用,也就是俗称的“十个茶杯八个盖”,那么,如果所有虚拟机内存使用量超过了320GB,会发生什么呢?
小E和小F讨论了半天也没有得到答案,两个人一块去问方老师。
方老师翻开了厚厚的《CSAPP》(Computer Systems: A Programmer's Perspective,没有购买链接,想买自己找去)
以Java语言为例,如果开发者需要一块64MB的内存,用于存放4096x4096的32位整型数组,在函数里面声明局部变量,运行的时候JVM是会死翘翘的。这是因为,局部变量在栈中分配,64MB远远超过栈尺寸,也就是产生了栈溢出。
小F跳起来:这里当然应该用
代码语言:javascript复制int [][] bigArray = new int [4096][4096];
来分配!
"那么,如果JVM执行new的时候,没有向操作系统申请到内存呢?"
“啊,我明白了!”
原来,无论是JAVA这样的在JVM中执行的语言,还是C这样直接编译为CPU指令执行的语言,向操作系统申请内存,都有可能由于内存不足而申请失败,操作系统返回空指针NULL (实际上为0地址,处理器访问这个地址会导致异常)。
那么,如果开发者遵循开发规范,对空指针进行了判断,那么程序会进入异常业务处理流程,乃至优雅退出。而如果没有对空指针进行判断,程序会直接抛出异常。
在虚拟化系统中,如果一个虚拟机出现内存不足的时候,如虚拟机只分配了8GB内存,而其中运行的应用需要大于8GB的内存,在操作系统没有交换分区(开启虚拟内存)的情况下,该虚拟机上的应用会报告内存不足,或异常退出。运维团队可以以此为依据,比较容易地定位问题所在。
而开启内存气球的情况,就有所不同了。
假如应用运维团队为虚拟机A和虚拟机B各分配了8GB内存,虚拟机A有8GB物理内存,而实际上给虚拟机B的只有4GB,并通过内存气球技术向虚拟机A偷用4GB。很快,虚拟机B上的应用把自己的4GB和虚拟机A的4GB内存都占满了,就像小F把小E的葵花子偷偷吃了一多半那样。此时,虚拟机A实际上只有4GB内存可用,但由于虚拟机A上的应用暂时没有申请更多内存,问题被掩盖了。
过了一会儿,虚拟机A上的应用需要继续申请内存,但由于hypervisor没有及时将内存气球中的内存归还给虚拟机A,虚拟机A上的应用申请内存失败,导致应用异常退出。运维团队只发现了虚拟机A上的应用异常退出,却没有办法发现这是因为虚拟机B通过内存气球从虚拟机A上偷用内存导致的。
也就是说,启用内存气球,虽然可以实现内存超分配,但实际上会让系统变得不稳定,而且,出现内存气球造成的应用异常时,故障是难以定位的!
小F想起来开头和小E说好的去吃辣子鸡,问方老师:一起去吃辣子鸡吗?
“啊,辣子鸡啊?那不去了。我还有问题要研究下。”
欲知方老师到底为什么不去吃辣子鸡,请看下回分解。