1、降低相关的内核参数;
2、通过Oracle参数来降低SGA;
3、通过减小sort_area_size来降低PGA。
在UNIX平台中,一般通过操作系统命令如“ps”或“top”来定位一个进程的内存大小。这些工具可以预警那些大量占用内存的进程。但是,这些工具统计出来的Oracle进程的内存情况往往是与实际PGA内存是有出入的。
这是为什么呢?有两种原因会导致错误的进程内存报告错误:将那些非进程私有的(如共享内存)计算进去了;操作系统没有回收空闲内存。下面详细解释它们。
统计了非私有内存
一个内存中的进程包含以下几个部分:
o共享内存(SGA)
o共享库(包括公用和私有的)
o私有数据(指数据段[DATA]或堆)
o可执行部分(指文本[TEXT]段)
而SGA和TEXT部分是被所有Oracle进程共享的。它们只会被映射到内存中一次,而不会未每个进程做映射。因此,这些内存不是一个新的Oracle进程导致的内存增加部分。
操作系统没有回收空闲内存
通常,一部分内存被一个进程释放了后,并没有立即返回到操作系统的空闲池中,而是继续保持与进程的关联,直到操作系统内存不足时,才会回收这些空闲页。所以操作系统工具报告的进程内存大小可能会比实际大。
从Oracle的角度来看,一个服务进程的私有内存包括多个Oracle“堆(heap)”。在Oracle的术语中,堆就是一个受管理的内存区。而对于操作系统来说,这仅仅是分配给一个应用程序的另外一块内存而已。PGA和UGA中都关联到堆。
Oracle当前或者曾经在这些堆中拥有的内存总数可以通过以下语句统计出来:
SQL> select statistic#, name, value
2 from v$sysstat
3 where name like '%ga memory%';
STATISTIC# NAME VALUE
---------- -------------------------- ------------------------------
20 session uga memory 8650004156
21 session uga memory max 778811244
25 session pga memory 50609488
26 session pga memory max 58007200
查询所有会话的堆大小可以用以下语句实现:
select value, n.name|| '('||s.statistic#||')' , sid
from v$sesstat s , v$statname n
where s.statistic# = n.statistic#
and n.name like '%ga memory%'
order by sid;
但是,查询出来大的PGA或UGA并不一定说明有问题。它们的大小受到以下参数影响:
oSORT_AREA_SIZE
oSORT_AREA_RETAINED_SIZE
oHASH_AREA_SIZE
另外,过多的使用PL/SQL结构体(如PL/SQL TABLE、ARRAY)也会导致会话内存增大。
2.2.Oracle的内存的分配、回收
Oracle中的共享内存区的分配都是以chunk为最小单位的。Chunk不是一个固定值,它是一个扩展段(extent)中一块连续的内存。而Oracle的内存(其他存储,如磁盘也是)的增长是以扩展段为基础的。
2.2.1.空闲列表和LRU链表
空闲的chunk按照大小来组织在相应的空闲列表(Free List)中。而未pin住的、可重建(unpinned recreatable)的chuck被维护在两个分别用于周期性chunk和短期chunk的LRU链表中。子堆还有一个包含少许空闲内存的主永久内存chunk。
2.2.2.空闲内存分配和回收
空闲内存都是由空闲列表(free list)统一管理、分配的。每个空闲的chunk(大块)都会属于也只属于一个空闲列表。空闲列表上的chunk的大小范围是由bucket来划分的。Bucket直译为“桶”,在西方,往往用桶来盛装一定品质范围的物品,以便查找。比如,在采矿时,用不同的桶来装不同纯度的矿石,在桶上标明矿石的纯度范围,以便在提炼时可以采用不同工艺。在这里,我们也可以把bucket视为一种索引,使Oracle在查找空闲块时,先定位所需的空闲块在哪个bucket的范围内,然后在相应的空闲列表中查找。
一次内存的分配过程如下:当一个进程需要一个内存的大块(chunk)时,它会先扫描目标空闲列表(每个空闲列表对应有一个bucket,bucket是这个空闲列表的中chunk的大小范围)以查找最适合大小的chunk。如果找不到一个大小正好合适的chunk,则继续扫描空闲列表中更大的chunk。如果找到的可用chunk比所需的大小大24字节或者更多,则这个chunk就会被分裂,剩余的空闲chunk又被加到空闲列表的合适位置。如果空闲列表中没有一个chunk能满足要求的大小,则会从非空的相邻bucket的空闲列表中取最小的chunk。如果所有空闲列表都是空的,就需要扫描LRU链表释放最近最少使用的内存。当chunk空闲时,如果相邻的chunk也是空闲的,它们可能会结合(coalesce)起来。
当所有空闲列表都没有合适的空闲chunk可用时,就开始扫描LRU链表,将最近最少使用的chunk释放掉(如有空闲的相邻chunk,则结合),放到相应的空闲列表种去,直到找到合适的chunk或者达到最大查找数限制。如果在释放了LRU链表中最近最少使用的chunk后没没有找到合适空闲chunk,系统就抛4031错误。
如果找到了可用空闲chunk,就将它从空闲列表中移出,放到LRU链表中去。
下面介绍一下Shared Pool的分配和回收。
2.2.3.SharedPool的分配和回收
在Shared Pool中,空闲列表扫描、管理和chunk分配的操作都是受到shared pool latch保护的。显然,如果shared pool含有大量的非常小的空闲chunk,则扫描空闲列表时间将很长,而shared pool latch则会要保持很久。这就是导致shared pool latch争用的主要原因了。有些DBA通过增加shared pool来减少shared pool latch争用,这种方法是没有用的,可能反倒会加剧争用(作为短期解决方法,可以flush shared pool;而要真正解决问题,可以采取在实例启动时keep住那些可能会断断续续使用的对象【这种对象最容易导致shared pool碎片】)。
只有执行了ALTER SYSTEM FLUSH SHARED_POOL才会使shared pool的空闲chunk全结合起来。因此,即使shared pool空闲内存之和足够大,也可能出现内存请求失败(空闲内存都是一些很小的碎片chunk)。
实际上,oracle实例启动时,会保留大概一半的Shared Pool,当有内存压力时逐渐释放它们。Oracle通过这种方法限制碎片的产生。Oracle的少量空闲内存(spare free memory)和X$表即其他持久内存结构一起,隐含在shared pool的主要持久内存chunk中。这些内存不在share