HEAP结构与ORA-4031

Oracle的各种内存组织都是以HEAP形式的,每个HEAP包含一个HEAP句柄和一系列的内存EXTENT,每个EXTENT包含了一系列的连续的CHUNK。当一个HEAP创建的时候,首先通过kghini调用来初始化HEAP句柄。一个HEAP句柄包括一组FREE LIST BUCKETS和相应的SIZE参数。调用kghini()的重要参数包括:heap的extent大小、heap的freelist桶的数量、分配大小、分配类型等。FREE LIST上链接了相同分配类型的的CHUNK。当正在使用的CHUNK释放的时候,会被放入到大小小于等于该CHUNK的FREE LIST上去,在同一个FREE LIST里,CHUNK不按照大小排序。比如:

可以看出,其中Bucket 0是存放所有小余76字节的CHUNK,Bucket 10存放所有大于32780字节的CHUNK。其他Bucket存放的CHUNK都是大于其SIZE大小,小于下一个Bucket的SIZE大小的CHUNK。另外一点我们要注意的是,Bucket中的CHUNK不是按照大小排序的。上面的例子是8.1.5的,不同的版本下,Bucket的数量会有所不同。

从共享池分配空间通过调用kghalo()来实现,这个调用返回指向CHUNK空间的指针。分配的空间的大小通过REQ_SIZE参数传递,但是堆管理会根据判断分配被称为ACTUAL_SIZE的空间,ACTUAL_SIZE可能比REQ_SIZE大或者小,不过并不是无规律的。从共享池分配空间的基本顺序如下:

首先搜索FREELIST上是否存在FREE的CHUNK;

如果没找到,第二步查找是否有RECREATABLE的CHUNK可以释放。已经使用的CHUNK被链接到LRU上,LRU链表中根据使用频繁程度,通过一个双向链来串联已经被分配的CHUNK。如果在LRU链中找到了可以释放的CHUNK,就通知HEAP MANAGER去释放,如果该CHUNK是被PIN住的,首先要UNPIN这些CHUNK,然后才能释放。HEAP MANAGER在释放内存时,会自动合并碎片。实际上这个过程我们做FLUSH SHARED_POOL操作是类似的,只是其规模要小的多,一旦释放了足够分配的CHUNK,那么这个释放过程就会结束;

如果第二步还没找到可以释放的CHUNK,就从父HEAP里分配空闲的空间;

如果父HEAP无法分配空间,那么就会报ORA-4031。

    从一个Chunk中分配了一部分内存,剩余的内存会根据其大小被放入相应的Bucket中供以后使用。根据HEAP的内存管理方法可以看出,共享池碎片化是不可避免的,随着数据库实例的启动时间的增长,这种碎片化趋势会越来越明显。不过共享池碎片化并不等于共享池就有问题,虽然说碎片化的共享池的效率可能会有所下降,但是一般情况下都在可以接受的范围内,因此DBA也不需要担心这个问题,不要谈虎色变。

按照这个算法,很多朋友可能会觉得,既然可以释放已有的对象获得所需的内存,那么就不应该再出现ORA-4031了吧。

实际上一切都没有那么完美。在释放共享池内存的时候,是有条件的,当前被PIN住的内存是不能释放的。如果某个CURSOR,当前正好有个会话正在执行,那么这个CURSOR相关的所有内存对象必须是被PIN住的,不能随便释放,否则这个SQL的执行就会出现问题。正因为这样,如果系统并发量很大的时候,很多共享池的内存是被PIN住,无法释放的,这样,共享池的空闲空间就被分给为无数个很小的碎片,对于一些较大的分配,可能就无法满足,出现ORA-4031也就不可避免了。

这种情况,我们可以认为共享池不足导致了问题,如果共享池的空间再大一点,出现内存不足的可能性也就会下降。除了共享池不足,还有什么情况可能导致ORA-4031呢?前面我们说的那种情况,是由于系统并发量很大,大量的共享池内存被PIN住而无法释放。这种情况,一旦系统负载下降,共享池就会逐渐恢复正常。另外一种情况就是,共享池中的有些LIST对象(比如LOCK,RESOURCE等),初始化分配一部分空间,如果这部分列表用完了,可以动态扩展。由于这些扩展是永久性的,所以,这些扩展就像是共享池中钉下的一颗颗的钉子,是无法拔掉的。如果这类的扩展十分频繁,那么时间长了,共享池就会被这些钉子分割为很多碎片。这种碎片化是永久性的,随着实例启动时间的推移,这种碎片化会越来越严重,而且无法自动或者手工修复。积累到一定程度,这个系统就会出现严重的碎片化问题,从而导致出现ORA-4031,甚至导致宕机。对于这种系统,一般来说可以通过三个渠道来优化:第一个渠道是加大共享池,使之碎片化的时间推迟,但是这只是延迟碎片化,不能解决碎片化;第二个渠道是定期重启实例,通过重启实例,恢复共享池的内存空间,确保系统正常运行,不出现宕机现象,不过要做到这一点,对于大多数7×24的系统来说十分困难;第三个是扩大经常动态扩展的列表对象的初始大小,减少其动态扩展的次数,最好能够让它们不再动态扩展,这个做法会浪费一定的共享池空间,但是是可以解决这个问题的最好的方法。

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值