all about Oracle memory - Oracle的内存管理

来源:互联网 发布:pxcook像素大厨 mac版 编辑:程序博客网 时间:2024/05/01 21:35

2.Oracle的内存管理

在这部分章节中,我们将从更底层的角度来了解Oracle的内存管理。当然,涉及到内存管理,就不可避免的要了解OS对内存的管理,在这一章节中,将谈到不少关于OS内存的管理知识。如果概念记得不清除了,可以找一本《操作系统》的书看看先。

2.1.Oracle内存管理基础

要了解内存管理,首先需要知道虚拟内存。而要了解虚拟内存,就先要知道CPU寻址。我们知道,目前主流的CPU都是32位或者64位的。那么这个位数指的是什么呢?就是指CPU的最大寻址能力。在CPU中,所有一切都是二进制表示的,那么一个32位CPU的寻址范围就是2^32=4G。但有可能一台机器的实际物理内存没有这么大,比如说只有2G。而程序员在编程的时候根本不用考虑实际物理内存多大,还是按照4G的内存来分配。这时,OS就提出了一个虚拟内存的概念,如果所寻址的数据实际上不在物理内存中,那就从“虚拟内存”中来获取。这个虚拟内存可以是一个专门文件格式的磁盘分区(比如UNIX下的swap分区),也可以是硬盘上的某个足够大的文件(比如win下的那个i386文件,好像是这个名字)。物理内存中长期不用的数据,也可以转移到虚拟内存中。这样的交换由OS来控制,用户看起来就好像物理内存大了一样。

虚拟内存寻址的一个好处就是可以使进程使用很大的虚拟内存地址,而无需考虑实际的物理内存的大小。这使得进程内存可以基于使用需要,从逻辑上分为几个不同段。这些段可能映射到不连续的虚拟内存地址上,以使内存能够增加。

为了共享RAM,需要有一个叫做交换磁盘(swap disk)的特殊磁盘空间。交换磁盘的重要目的是保存程序的通过LRU算法换出的内存,这样可以使很多程序能够共享有限的RAM。一旦非活动程序的RAM页被写入交换磁盘(Page Out),操作系统可以使空出来的内存用于其他活动的程序。如果非活动程序稍后又继续执行,被page out的RAM页又重新从交换磁盘中载入RAM(Page in)。这种重新载入RAM页的动作就叫交换,而交换是非常消耗时间的,并且会降低相关程序的性能。

尽管交换磁盘确保并发的RAM使用量能大于实际的RAM总理,但是为了确保最佳性能,交换空间绝不要被活动程序所使用。这是因为从交换磁盘上读取page out的RAM页要比直接从RAM中读取内存页要慢14000倍。磁盘访问都是以毫秒记的,而RAM访问是以10亿份之一秒记的。

2.1.1.Oracle的内存段类型

段(Segement)在OS上是对不同内存的使用目的和存放位置不同的区分。和一般的程序一样,Oracle使用以下几种段类型:

o程序文本(Pragram Text

文本段包括了程序本身的可执行的机器代码(除动态链接库以外)。文本段一般标识为只读,因此它能被多个进程共享来跑同一个程序。

o初始化全局数据(Initialized Global Data

这一段包括了被编译器初始化的全局数据,比如用于跟踪数据的字符串。初始化数据能被修改,因此它不能被运行同一程序的多个进程共享。Oracle很少使用这个段。

o未初始化全局数据(Uninitialized Global Data

未初始化全局数据一般称为BSS(Block Started bySymbol 以符号开始的块)段。这一段包括了静态分配的全局数据,这些数据在进程运行时被进程初始化。Oracle也很少使用这个段。

o数据堆(Data Heap

数据堆被用于进程在运行时,通过使用系统调用malloc()或sbrk()动态分配内存。Oracle将数据heap用于PGA。

o执行堆栈(Execution Stack

无论什么时候一个函数被调用,它的参数和返回上下文被push到一个执行堆栈中。返回上下文实际上是一组CPU注册值,这些注册值描述了进程在调用函数时那一刻的状态。当调用结束后,堆栈被POP而上下文被保留,以使执行能从函数调用时的结构状态立即执行下去。堆栈同时还保留了代码块的本地变量。堆栈大小依赖于函数嵌套或递归调用的深度、参数和本地变量所需的内存大小。

o共享库(Shared Libraries

共享库是一个与位置无关的可执行代码集,这个集合实现了许多程序——特别是系统调用功能——所需要的功能。共享库段也是只读的,它被所有的进程(包括Oracle进程)共享。共享库无需保存一份在内存中。当调用了共享库中的一个函数后,进程需要打开共享库文件,然后通过系统调用mmap()将它映射到它的地址空间去。

使用共享库的另外一种方法是在程序文本段本身将需要的系统调用include进去。在那些不支持共享库的操作系统中或用上面方式有问题时就需要这样做。在大多数操作系统中,Oracle使用共享库作为来实现系统调用而不是实现Oracle代码本身。然而,Java类库都是编译好的,并且作为共享库动态链接的。

o共享内存段(Shared Memory Segment

共享内存允许关联的进程共同读写内存中的同样数据。每个需要在共享内存段中寻址的进程都需要先将这段内存附到它自己的虚拟内存地址中去(一般通过shmat()系统调用实现)。Oracle SGA就是使用的共享内存段。

2.1.2.Oracle的内存管理模块

Oracle中有两个内存管理模块。一个是内核服务内存管理模块(KernelService Memory KSM);一个是内核通用堆管理模块(Kernel Generic Heap KGH)

在X$表中,有两种用于这两个模块的表,它们就是以KSM和KGH开头的表。这两个模块相互非常紧密。内存管理模块是负责与操作系统进行接口以获取用于Oracle的内存,同时还负责静态内存的分配。这个模块中比较重要的X$表是X$ksmfs,它记录了固定sga、buffer cache、log buffer在内核服务内存中的分配。而堆管理模块则负责动态内存的管理。这也就是为什么SGA和PGA中堆又叫可变内存区了。SharedPool、Library cache和PGA的堆都是由这个模块管理的。

一个堆(Heap)包括一个堆描述符和一个或多个内存扩展段(extent)。一个堆还可以包含子堆(Subheap)。这种情况下,堆描述符和子堆的扩展段可以被视为其父堆的大块(chunk)。堆描述符的大小依赖于堆的类型和堆的空闲列表和LRU列表所包含的列表(Header)头的多少。一个扩展段又一个包含指向前一个和后一个扩展段指针(Pointer)的小的头部,扩展段的其他内存就是堆可用于动态分配的内存。

除了还包含一个保留列表的这一特性外,Shared Pool中的子堆具有与Shared Pool本身相同的结构。内存是以Chunk为单位分配的。空闲的chunk按照大小来组织在相应的空闲列表(Free List)中。而未pin住的、可重建(unpinned recreatable)的chuck被维护在两个分别用于周期性chunk和短期chunk的LRU链表中。子堆还有一个包含少许空闲内存的主永久内存chunk。子堆也许还包含子堆,一共可以嵌套到四层。

子堆的概念非常重要,因为大多数被缓存在shared pool中的对象实际上都被保存在子堆中,而不是保存在最上一层的堆中。在一个子堆中寻找空闲空间就相当于在shared pool中寻找空闲空间。它们的不同处就在于子堆可以通过分配新的扩展段(extent)来增长,而shared pool具有固定的扩展段数(10g引入SGA自动管理特性后,shared pool的扩展段数也是可变的)。为子堆分配新的扩展段受到扩展段大小的限制,因此会存在这样的可能,因为没有任何一个父堆可以分配一个所需最小扩展段大小的chunk导致在子堆中查找一个小的chunk失败(抛4031错误)。

为了减少内存错误,在10g中,引入了多个隐含参数对扩展段进行控制,

_bct_buffer_allocation_min_extents 每次buffer cache分配时的最小扩展段数(1)。

_compilation_call_heap_extent_size 编译调用时分配堆的扩展段的大小(16384)10gR2引入。

_kgl_fixed_extents library cache内存分配时是否使用固定的扩展段大小(TRUE),10gR2引入。

_mem_std_extent_size 固定扩展段大小的堆的标准扩展段大小(4096),10gR2引入。

_minimum_extents_to_shrink 当内存收缩时,收缩的最少扩展段数(1

_total_large_extent_memory 分配大扩展段的内存数(0),10gR1引入,10gR2中已经废除。

_pga_large_extent_size(1048576)和_uga_cga_large_extent_size(262144)控制了当使用系统函数mmap()初始化时,PGA、UGA和CGA扩张段的最大大小。

2.1.3.内存分配颗粒Granule

在Oracle的内存分配和管理中,有一个重要的单位:Granule(颗粒)。granule是连续虚拟内存分配的单位。Granule的大小依赖于SGA的总的大小(即SGA_MAX_SIZE大小)。当SGA小于128M时,granule为4M,SGA大于128M时,granule为16M。

Buffer Cache、Shared Pool、Large Pool和Java Pool(在10g中,还包括Streams Pool、KEEP buffer cache、RECYCLE buffer cache、nK Buffer Cache、ASM Buffer Cache)的增长和收缩都是以granule为单位的。SGA的各个组件的大小增长、收缩情况可以通过视图v$sga_dynamic_components(这个视图在1.1.2中有介绍)来观察。

在实例启动时, Oracle先分配granule条目(Entry),使所有granule能支持到SGA_MAX_SIZE的空间大小。如果没有设置PRE_PAGE_SGA和LOCK_SGA,在实例启动时,每个组件请求它所需要的最少granule。

因此最小SGA(占用物理内存)是3个granule,包括:

o固定SGA(包括redo buffer)一个granule

oBuffer Cache一个granule

oShared Pool一个granule

 

我们可以通过“ALTER SYSTEM”命令来修改分配给各个组件的granule数。当DBA想要给组件增加granule时,需要考虑实例中是否还有足够的granule来分配给新增加的组件大小(即增大后,各个组件之和小于SGA_MAX_SIZE)。有一点要注意,ALERT SYSTEM指定的是新的组件大小,是内存的大小,不是granule数。而Oracle在分配内存时,会以granule为单位,如果新分配的大小不是granule大小的倍数,则会使用最接近且大于分配数的granule的倍数值。例如,Unix中,当granule为16M,新分配组件大小为120M(不是16的倍数),Oracle则会实际分配128M(16×8);32位windows下,SGA小于1G时granule是4M,大于1G时granule是8M。

执行ALERT SYSTEM扩展组件大小后,前台进程(即执行ALTERSYSTEM命令)的进程会将SGA中的可用的granule(按照扩展新增大小计算)先保留下来。当这些granule被保留后,前台进程将后续处理交给后台进程。后台进程负责将这些保留的granule加到指定的组件的granule列表中去。这就完成了一次对SGA组件的扩展。

Granule的默认大小是根据以上规则规定的,但也可以通过隐含参数_ksm_granule_size来修改(强烈不建议修改)。另外,隐含参数_ksmg_granule_locking_status可以设置内存分配是否强制按照granule为单位进行分配。

2.1.4.SGA内存

当一个Oracle实例启动后,主要的SGA区的大小一开始基于初始化参数计算得出。这些大小可以通过show sga显示(实例启动后也会显示出这些信息)。但是,在共享内存段分配之前,每个区(Area)的大小都只有大概一个内存页大小。当需要时,这些区被分为一些子区(sub-area),因此没有一个子区会大于操作系统所限制的共享内存段(UNIX下受SHMMAX限制)的大小。对于可变区,有一个操作系统规定的最小子区大小,因此可变区的大小是最小子区大小的倍数。

如果可能,Oracle会为整个SGA分配一个单独的共享内存段。然而,如果SGA大于操作系统限制的单个共享内存段的大小时,Oracle会使用最佳的算法来将所有子区组织在多个共享段中,并且不超过SGA最大大小的限制。

严重的页入/页出会导致很严重的系统性能问题。然而,大内存消耗导致的间断的页入/页出是没有影响的。大多数系统都有大量的非活动内存被page out而没有什么性能影响。但少量的页出也是有问题的,因为这会导致SGA中那些中度活性的页会经常page out。大多数操作系统提供了一个让Oracle锁住SGA(设置lock_sga参数为TRUE)到物理内存中的机制以防止page out。在某些操作系统中,oracle需要有特定的系统权限来使用这一特性。

当共享池分配了一个chunk,代码会返回给执行分配的函数一个注释。这些注释可以通过表X$KSMSP的字段KSMCHCOM查到。它们同时描述了这块内存分配的目的。如以下语句:

select ksmchcom, ksmchcls, ksmchsiz from x$ksmsp;

2.1.5.进程内存

进程包括程序代码(文本)、本地数据域(进程堆栈、进程堆【主要是PGA】和进程BSS【未初始化的全局数据】)和SGA。程序代码的大小由基本内核、内核、联机的网络情况以及所使用的操作系统决定的。SGA大小是由Oracle初始化参数决定的。而这两部分是共享。随着用户的增加,它们与单个Oracle服务进程的关系越来越小。它们是可以通过修改Oracle配置参数来改变的。

本地数据域中的堆栈是根据需要来增大、缩小的。然而,堆就不会释放内存了。堆的主要组成部分是PGA。而影响PGA的主要因素是sort_area_size(如果没有配置PGA_AGGREGATE_TARGET的话)。因此,非自动PGA内存管理模式下,可以通过控制sort_area_size来控制PGA的大小。

因此,总的来说,可以有以下方法来限制进程内存大小:

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中。这些内存不在shared pool的空闲列表中,因此能够立即被分配。但是,却包含在视图V$SGASTAT的free memory的统计数据中。

SQL> select * from v$sgastat
  2  where pool = 'shared pool'
  3  and name = 'free memory';
POOL         NAME                            BYTES
------------ -------------------------- ----------
shared pool  free memory                  18334468

而spare free memory可以用以下方式查出:

select
  avg(v.value)  shared_pool_size,
  greatest(avg(s.ksmsslen) - sum(p.ksmchsiz), 0)  spare_free,
  to_char(
    100 * greatest(avg(s.ksmsslen) - sum(p.ksmchsiz), 0) / avg(v.value),
    '99999'
  ) || '%' wastage
from
  sys.x$ksmss s,
  sys.x$ksmsp p,
  sys.v$parameter v
where
  s.inst_id = userenv('Instance') and
  p.inst_id = userenv('Instance') and
  p.ksmchcom = 'free memory' and
  s.ksmssnam = 'free memory' and
  v.name = 'shared_pool_size';
 

注意:如果10g中用了SGA内存自动管理。以上语句可能无法查出。

当需要时,少量的空闲内存chunk会被释放到shared pool中。除非所有这些少量空闲内存被耗尽了,否则不会报4031错误。如果实例在负载高峰运行了一段时期之后还有大量的少量空闲内存,这就说明sharedpool太大了。

 

而未pin住的、可重建(unpinned recreatable)的chuck被维护在两个分别用于周期性chunk和短期chunk的LRU链表中。这两个LRU链表的长度可以通过表X$KGHLU查到,同时还能查到被flush的chunk数、由于pin和unpin而加到和从LRU链表中移出的chunk数。X$KGHLU还能显示LRU链表没有被成功flush的次数,以及最近一次这样的请求失败的请求大小。

SQL> column kghlurcr heading "RECURRENT|CHUNKS"
SQL> column kghlutrn heading "TRANSIENT|CHUNKS"
SQL> column kghlufsh heading "FLUSHED|CHUNKS"
SQL> column kghluops heading "PINS AND|RELEASES"
SQL> column kghlunfu heading "ORA-4031|ERRORS"
SQL> column kghlunfs heading "LAST ERROR|SIZE"
SQL> select
  2  kghlurcr "RECURRENT_CHUNKS",
  3  kghlutrn "TRANSIENT_CHUNKS",
  4  kghlufsh "FLUSHED_CHUNKS",
  5  kghluops "PINS AND_RELEASES",
  6  kghlunfu "ORA-4031_ERRORS",
  7  kghlunfs "LAST ERROR_SIZE"
  8  from
  9  sys.x$kghlu
 10  where
 11  inst_id = userenv('Instance');
RECURRENT_CHUNKS TRANSIENT_CHUNKS FLUSHED_CHUNKS PINS AND_RELEASES ORA-4031_ERRORS LAST ERROR_SIZE
---------------- ---------------- -------------- ----------------- --------------- ---------------
             327              368              0              7965               0               0
             865              963           2960            102138               0               0
            1473             5657             96             20546               1             540
               0                0              0                 0               0               0

如果短期链表的长度大于周期链表的长度3倍以上,说明SharedPool太大,如果chunk flash对LRU 操作的比例大于1/20,则说明Shared Pool可能太小。

2.3.Oracle在UNIX下的内存管理

在UNIX下,Oracle是以多进程的方式运行的。除了几个后台进程外,Oracle为每个连接起一个进程(进程名称中,LOCAL = NO)。而UNIX下的内存分为两类,一种叫共享内存,即可以被多个进程共享;还有一种就是私有进程,只能被所分配的进程访问。根据Oracle内存区的不同特性,SGA内存可以被所有会话进程共享,因此是从共享内存中分配的;而PGA是进程私有的,因而是从私有内存中分配的。

2.3.1.共享内存和信号量

在Unix下,Oracle的SGA是保存在共享内存中的(因为共享内存是可以被多个进程共享的,而SGA正是需要能被所有Oracle进程所共享)。因此,系统中必须要有足够的共享内存用于分配SGA。而信号量则是在共享内存被多个进程共享时,防止发生内存冲突的一种锁机制。

UNIX下,对于内存的管理配置,是由许多内核参数控制,在安装使用Oracle时,这些参数一定要配置正确,否则可能导致严重的性能问题,甚至Oracle无法安装、启动。涉及共享内存段和信号量的参数:

参数名称

建议大小(各个平台的Oracle建议值可以去metalink上找)

参数描述

SHMMAX

可以得到的物理内存

(0.5*物理内存)

定义了单个共享内存段能分配的最大数.

SHMMAX必须足够大,以在一个共享内存段中能足够分配Oracle的SGA空间.这个参数设置过低会导致创建多个共享内存段,这会导致Oracle性能降低.

SHMMIN

 

定义了单个共享内存段能分配的最小数.

SHMMNI

512

定义了整个系统共享内存段的最大数。

NPROC

4096

系统的进程总数

SHMSEG

32

定义了一个进程能获取的共享内存段的最大数.

SEMMNS

(NPROC * 2) * 2

设置系统中的信号数.

SEMMNS的默认值是128

SEMMNI

(NPROC * 2)

定义了系统中信号集的最大数

SEMMSL

与Oracle中参数processes大小相同

一个信号集中的信号最大数

SEMMAP

((NPROC * 2) + 2)

定义了信号映射入口最大数

SEMMNU

(NPROC - 4)

定义了信号回滚结构数

SEMVMX

32768

定义了信号的最大值

 

信号量(Semaphore):对于每个相关的process都給予一个信号來表示其目前的狀态。主要的目地在于确保process间能同步,避免process存取shared data时产生碰撞(collisions)的情況。可以把信号量视为操作系统级的用于管理共享内存的钥匙,每个进程需要有一把,一个信号量集就是一组钥匙,这组钥匙可以打开共同一个共享内存段上的锁。当一个进程需要从共享内存段中获取共享数据时,使用它自己的钥匙打开锁,进入后,再反锁以防止其他进程进来。使用完毕后,将锁释放,这样其他进程就可以存取共享数据了。

共享内存(Shared Memory是指同一块内存段被一个以上的进程所共享。这是我们所知速度最快的进程间通讯方式。使用共享内存在使用多CPU的机器上,会使机器发挥较佳的效能。 

 

可以通过以下命令检查你当前的共享内存和信号量的设置:

$ sysdef | more 

 

当Oracle异常中止,如果怀疑共享内存没有被释放,可以用以下命令查看:

$ipcs -mop
IPC status from /dev/kmem as of Thu Jul  6 14:41:43 2006
T      ID     KEY        MODE        OWNER     GROUP NATTCH  CPID  LPID
Shared Memory:
m       0 0x411c29d6 --rw-rw-rw-      root      root      0   899   899
m       1 0x4e0c0002 --rw-rw-rw-      root      root      2   899   901
m       2 0x4120007a --rw-rw-rw-      root      root      2   899   901
458755 0x0c6629c9 --rw-r-----      root       sys      2  9113 17065
m       4 0x06347849 --rw-rw-rw-      root      root      1  1661  9150
m   65541 0xffffffff --rw-r--r--      root      root      0  1659  1659
524294 0x5e100011 --rw-------      root      root      1  1811  1811
851975 0x5fe48aa4 --rw-r-----    oracle  oinstall     66  2017 25076

 

然后用ID号清除共享内存段:

$ipcrm -m 851975

 

对于信号量,可以用以下命令查看:

$ ipcs -sop
IPC status from /dev/kmem as of Thu Jul  6 14:44:16 2006
    ID     KEY        MODE        OWNER     GROUP
Semaphores:
s       0 0x4f1c0139 --ra-------      root      root
... ...
s      14 0x6c200ad8 --ra-ra-ra-      root      root
s      15 0x6d200ad8 --ra-ra-ra-      root      root
s      16 0x6f200ad8 --ra-ra-ra-      root      root
s      17 0xffffffff --ra-r--r--      root      root
s      18 0x410c05c7 --ra-ra-ra-      root      root
s      19 0x00446f6e --ra-r--r--      root      root
s      20 0x00446f6d --ra-r--r--      root      root
s      21 0x00000001 --ra-ra-ra-      root      root
s   45078 0x67e72b58 --ra-r-----    oracle  oinstall

 

当Oracle异常中止,可以根据信号量ID,用以下命令清除信号量:

$ipcrm -s 45078

 

一个共享内存段可以可以分别由以下两个属性来定位:

oKey:一个32位的整数

o共享内存ID:系统分配的一个内存

$ ipcs -mop
IPC status from /dev/kmem as of Thu Jul  6 10:32:12 2006
T      ID     KEY        MODE        OWNER     GROUP NATTCH  CPID  LPID
Shared Memory:
m       0 0x411c29d6 --rw-rw-rw-      root      root      0   899   899
m       1 0x4e0c0002 --rw-rw-rw-      root      root      2   899   901
m       2 0x4120007a --rw-rw-rw-      root      root      2   899   901
458755 0x0c6629c9 --rw-r-----      root       sys      2  9113 17065
m       4 0x06347849 --rw-rw-rw-      root      root      1  1661  9150
m   65541 0xffffffff --rw-r--r--      root      root      0  1659  1659
524294 0x5e100011 --rw-------      root      root      1  1811  1811
851975 0x5fe48aa4 --rw-r-----    oracle  oinstall     66  2017 25076

2.3.2.私有内存

对于PGA,由于是分配的私有内存,不存在争用问题,因而OS也没有相应的信号量来控制(同理,PGA中也没有latch)。在10g之前,PGA的私有内存是通过函数malloc()和sbrk()进行分配扩展的,10g之后,私有内存是通过函mmap()进行初始化的。

另外,还有一点要注意的是,和SGA不同,PGA内存的大小不是固定的,是可以扩展的(前者的大小是固定的,无法增长的)。因而进程的私有内存是会增长的。因此,一个规划好的系统发生内存不足的情况通常是由于进程的私有内存或进程数量突然增长造成的(PGA_AGGREGATE_TARGET参数能尽量控制但不保证PGA内存总量被控制在一定数值范围内)。

隐含参数_pga_large_extent_size(1048576)和_uga_cga_large_extent_size(262144)就控制了当初始化时,PGA、UGA和CGA扩张段的最大大小。

2.3.3.SWAP的保留区

swap(交换)区,是UNIX中用来分配虚拟内存的一块特殊的磁盘分区。UNIX启动每一个进程,都需要在swap区预留一块和内存一样大小的区域,以防内存不够时作数据交换。当预留的swap区用完时,系统就不能再启动新的进程。比如,系统物理内存是4G,而设置的交换区只有1G,那么可以计算得出大概3G的内存会浪费(Buffer Cache除外,可能有2G浪费)。 

 

在HP-UX中,参数swapmen_on可以让系统创建一个pseudo-swap(伪交换区),大小为系统物理内存的3/4,但是这个伪交换区并不占用任何内存和硬盘资源。只是说,让系统认为,交换区的大小是1+4*3/4=4G,而不是1G,就是说可以启动更多的进程,避免内存的浪费。

一般系统物理内存不大的时候,设置交换区是物理内存的2-4倍,swapmen_on设置为1或0都没什么影响,但是当系统内存很大如8G时,因为swap一般不设为16G-32G,这时开启swapmen_on就很必要了。

hp建议,即使设置了swapmen_on,也将你的swap为物理内存的1-1.5倍。

 

swap大小设置不当,也同样会造成系统的性能问题。因为,swap中首先会为各个进程留出一个保留区,这部分区去掉后,swap的可用大小就比较小了(这就是为什么用swapinfo可能看到Total PCT USED为100%而devPCT USED为0%)。当swap可用区不足,而由内存需要被page out到swap区中,就需要先将swap区中一些页被page in到物理内存中去,因而导致发生交换,产生性能问题。

 

swap的使用情况可以通过swapinfo查看:

> swapinfo -mt
             Mb      Mb      Mb   PCT  START/      Mb
TYPE      AVAIL    USED    FREE  USED   LIMIT RESERVE  PRI  NAME
dev        4096       0    4096    0%       0       -    1  /dev/vg00/lvol2
dev        8000       0    8000    0%       0       -    1  /dev/vg00/swap2
reserve       -   12026  -12026
memory    20468   13387    7081   65%
total     32564   25413    7151   78%       -       0    -

2.4.Oracle在windows下的内存管理

2.4.1.Windows内存系统概述

Windows NT使用一个以页为基础的虚拟内存系统,该系统使用32位线性地址。在内部,系统管理被称为页的4096字节段中的所有内存。每页的物理内存都被备份。 对于临时的内存页使用页文件(pagefile),而对于只读的内存页,则使用磁盘文件。在同一时刻,最多可以有16个不同的页文件。代码、资源和其它只读数据都是通过它们创建的文件直接备份。

Windows NT为系统中的每一个应用程序(进程)提供一个独立的、2 GB的用户地址空间。对于应用程序来说,好象是有2 GB的可用内存,而不用考虑实际可用的物理内存的量。如果某个应用程序要求的内存比可用的内存更多时,Windows NT是这样满足这种要求的,它从这个和/或其他的进程把非关键内存分页(paging)到一个页文件,并且释放这些物理内存页。结果,在Windows NT中,全局堆不再存在。相反,每一个进程都有其自己的32位地址空间,在其中,该进程的所有内存被分配, 包括代码、资源、数据、DLL(动态链接库),和动态内存。实际上,系统仍然要受到可用的硬件资源的限制,但是实现了与系统中应用程序无关的、对于可用资源的管理。

Windows NT在内存和地址空间之间作出了区分。每个进程分配到2 GB的用户地址空间,而不管对于该进程的实际可用物理内存有多少。而且,所有进程都使用相同范围的线性32位地址,范围从0000000016-7FFFFFFF16,而不考虑可用内存的地址。Windows NT负责在适当的时间把内存页映射(paging)到磁盘以及从磁盘页映射回内存,使得每个进程都确保能够寻址到它所需要的内存。尽管有可能出现两个进程试图同时访问同一虚拟地址上的内存,但是,实际上Windows NT虚拟内存管理程序是在不同的物理位置描述这两个内存的位置。而且这两个地址都不见得与原始的虚拟地址一致。这就是虚拟内存。

Win32环境下,32位的地址空间转化为4GB的虚拟内存。默认情况下,将一半(2GB)分配给用户进程(因而一个进程的最大可用虚拟内存为2G,oracle进程同样受此限制),另一半(2GB)分配给操作系统。

因为虚拟内存的存在,一个应用程序能够管理它自己的地址空间,而不必考虑在系统中对于其它进程的影响。在WindowsNT中的内存管理程序负责查看在任何给定的时间里,所有的应用程序是否有足够的物理内存进行有效的操作。WindowsNT操作系统下的应用程序不必考虑和其它应用程序共享系统内存这个问题。并且,即使在应用程序自己的地址空间内,它们仍能够与其它的应用程序共享内存。

区分内存和地址空间的一个好处是,为应用程序提供了将非常大的文件加载到内存的能力。不必将一个大的文件读进内存中,WindowsNT为应用程序保留该文件所需的地址范围提供了支持。然后,在需要的时候,该文件部分就可以被浏览了(物理性地读进内存)。通过虚拟内存的支持,对于大段的动态内存的分配同样可以做到这一点。

在任意给定的时间,进程中每个地址都可以被当作是空闲的(free)、保留的(reserved)或已提交的(committed)。进程开始时,所有地址的都是空闲的,意味着它们都是自由空间并且可以被提交到内存,或者为将来使用而保留起来。在任何空闲的地址能够被使用前,它必须首先被分配为保留的或已提交的。试图访问一个保留的或已提交的地址都将产生一个访问冲突异常(access violation exception)。

一个进程中的所有2 GB的地址要么为了使用而是空闲的、要么为了将来的使用而是保留的、要么已提交到特定的内存(在使用的)。

一旦地址被以保留的或者已提交的形式分配,VirtualFree是唯一可以释放它们的方法棗那就是,将它们返回到自由的地址。VirtualFree还可以用来对已提交的页解除提交,同时,返回这些地址到保留状态。当解除地址的提交时,所有与该地址相关的物理内存和页文件空间都被释放。

在Windows NT中的进程有一个被称为工作组(working set)的最小页,是为了进程能够顺利地运行,在运行时在内存中必须被提供。WindowsNT在启动时为一个进程分配了默认数量的页数,并且逐渐地调整该数,使得系统中所有激活的进程的性能达到一种平衡的最优。当一个进程正在运行时(实际上是,是一个进程的线程正在运行时),Windows NT“尽量”确保该进程的工作组页总是驻留在物理内存中。工作集即在物理内存中保持的虚拟页面的子集,分进程工作集和系统工作集。

2.4.2.Windows下Oracle的内存配置

在windows下,Oracle实例作为一个单独的进程运行,这个进程是一个标准的Win32应用程序,它能够申请最大为2G的虚拟内存地址空间,所有用户连接后后台线程的分配内存(包括像buffer cache那些全局内存)必须小于2G(64位平台中无此限制,32位平台中可以通过设置参数use_indirect_data_buffers来突破这一限制,不详述)。

Oracle可以运行于windows NT下的任何版本,但是Oracle一般不推荐将Oracle数据库运行在PDC(主域控服务器)或BDC(备域控服务器)下。这是因为域控服务器会需要大量的文件缓存(这会消耗大量内存,影响到Oracle)和网络资源。

而文件缓存带来的另外一个问题就是,这个机器到底是作为一个专用的数据库服务器还是一个混合服务器。因为Oracle数据库不会用到文件缓存(log buffer就相当于Oracle自己的文件缓存),它通过直接写磁盘来避免文件缓冲。

在专用数据库服务器系统上,用户一定要确保没有使用到页文件(pagefile即虚拟内存文件)。否则,可以通过修改Oracle参数或者增大物理内存来避免。如果大量额页被持续的移入、移出到虚拟内存中,会严重影响到性能。

如果是专用服务器系统,有以下建议:

o如果分配给Oracle的总内存能保证不会超过物理内存,则虚拟内存页可以被设置位物理内存的50%,并且可以增长到物理内存的100%大小(在my computer => properties => Advanced => Performance => Settings=> Advanced => Virtual Memory => Change中设置,设置Initialsize为物理内存的50%,Maximum Size和物理内存大小相同);

o对于主要是运行纯Oracle数据库的系统(但不是专用),一般推荐虚拟内存页大小在1倍到1.5倍于物理内存大小之间;

o对于物理内存大于2G的机器,要求虚拟内存页最少为2G。

一个机器上能被用于分配的总内存等于物理内存加上扩展前的虚拟内存页大小。你一定要避免设置参数如buffer_cache_size或其他相关参数导致Oracle要求分配内存大于物理内存。尽管Oracle的分配内存大小是限制在总内存(物理内存+最小虚拟内存)之内,但是,对虚拟内存页的访问是非常慢的,会直接影响到系统性能,因此Oracle分配内存要小于物理内存大小以避免发生内存交换。

如果系统是混合应用,除了Oracle数据库外还运行了其他程序,这时就需要考虑设置虚拟内存页大于物理内存了。那些当前不活动的进程可以减少它们的工作区(working set 即物理内存)以使活动进程能增加工作区。如果Oracle数据库运行在这样的机器上,则推荐虚拟内页最少1.5到2倍的物理内存大小,特别是在内存大于2G时。

在Oracle 8.1.x之前,启动Oracle服务时不会启动Oracle实例。此时(即只启动了Oracle服务,没有启动实例),分配给Oracle.EXE的主要内存是给相关DLL的,大概20M(各个版本的DLL不同,因此占用内存情况也不同)。9i之后,Oracle实例会随着服务启动,不过我们可以在服务启动后再关闭实例,这时就可以观察出Oracle服务所占用的内存大小了:

 

这是windows下一个Oracle 10g的实例关闭后内存占用情况。我们看到此时Oracle服务占用的内存是32M。

 

Windows下,可以使用以下语句来计算Oracle占用的虚拟内存大小:

select sum(bytes)/1024/1024 + 22/*DLL占用内存*/ Mb 
from (select bytes from v$sgastat -- SGA内存
        union
        select value bytes from -- 会话内存
             v$sesstat s,
             v$statname n
        where
             n.STATISTIC# = s.STATISTIC# and
             n.name = 'session pga memory'
        union
        select 1024*1024*count(*) bytes -- 线程堆栈
        from v$process
       );

 

在实例启动时,所有全局内存页都被保留和提交(所有共享全局区、Buffer Cache和Redo Buffer)——可以通过TopShow观察到实例启动后,所需要的Page File都已经被分配。但只有一小部分内存页(如果没有设置PRE_PAGE_SGA的话;这小部分内存以granule为单位,固定SGA【包括redo buffer】一个、Buffer Cache一个、Shared Pool一个)被触及(touch)而已经分配到工作组(working set)中,而其他更多的页需要使用时才分配到工作组中。

通过设置注册表可以设置Oracle进程的最小工作组大小和最大工作组大小:

oORA_WORKINGSETMIN或ORA_%SID%_WORKINGSETMIN:Oracle.EXE进程的最小工作组大小(M为单位)

oORA_WORKINGSETMAX或ORA_%SID%_WORKINGSETMAX:Oracle.EXE进程的最大工作组大小(M为单位)

这些注册项需要加在HKEY_LOCAL_MACHINE -> SOFTWARE -> ORACLE或者HKEY_LOCAL_MACHINE -> SOFTWARE -> ORACLE -> HOMEn下。

在混合服务器下,这种设置能防止Oracle进程的内存被其他进程争用。设置这些注册项时,需要考虑PRE_PAGE_SGA的设置。如前所述,PRE_PAGE_SGA使Oracle实例启动时“触及”所有的SGA内存页,使它们都置入工作组中,但同时会增长实例启动时间。

ORA_WORKINGSETMIN是一个非常有用的参数,它能防止Oracle进程的工作组被缩小到这一限制值之下:

o如果设置了PRE_PAGE_SGA,实例启动后,工作组就大于这个限制值。在实例关闭之前,就不会低于这个值;

o如果没有PRE_PAGE_SGA,当实例的工作组一旦达到这个值后,就不会再低于这个值。

另外,在10g之前,存在一个Bug(642267),导致在windows系统中设置了LOCK_SGA后,实例启动报ORA-27102错误。可以通过设置ORA_WORKINGSETMIN最小为2M来解决这个问题。但是,windows中,LOCK_SGA只影响Oracle进程中的SGA部分,而分配给用户会话的内存不会受影响,这部分内存还是可能会产生内存交换。

2.4.3.SGA的分配

当实例启动时,oracle在虚拟内存地址空间中创建一段连续的内存区,这个内存区的大小与SGA所有区相关参数有关。通过调用Win32 API接口“VirtualAlloc”,在接口函数的参数中指定MEM_RESERVE | MEM_COMMIT内存分配标识和PAGE_READWRITE保护标识,这部分内存始终会被保留和提交。这就保证所有线程都能访问这块内存(SGA是被所有线程共享的),并且这块内存区始终有物理存储(内存或磁盘)所支持。

VirtualAlloc函数不会触及这块区域内的内存页,这就是说分配给SGA组件(如buffer cache)的内存在没有触及之前是不会到工作组中去的。

 

VirtualAlloc

VirtualAlloc函数保留或提交调用此函数的进程的虚拟内存地址空间中的一段内存页。如果没有指定MEM_RESET,被这个函数分配的内存自动初始化为0,

 

Buffer Cache通常被创建为一个单一的连续内存区。但这并不是必须的,特别是当使用了非常大的Buffer Cache时。因为dll内存和线程分配的内存会导致虚拟地址空间产生碎片。

当一个进程创建后,windows NT会在进程的地址空间中创建一个堆(heap),这个堆被称为进程的默认堆。许多Win32 API调用接口和C运行调用接口(如malloc、localalloc)都会使用这个默认堆。当需要时,进程能在虚拟内存地址空间中创建另外的命名堆。默认堆创建为1M大小(被保留和提交的)的内存区,当执行分配或释放这个堆的操作时,堆管理器提交或撤销这个区。而访问这个区是通过临界区来串行访问的,所以多个线程不能同时访问这个区。

当进程创建了一个线程后,windows NT会为线程堆栈(每个现场都有自己的堆栈)保留一块地址空间区域,并提交一部分这些保留区。当一个进程连接到标准区时,系统为堆栈保留一个1M大小的虚拟地址空间并提交这个区顶部的两个页。当一个线程分配了一个静态或者全局变量时,多个线程可以同时访问这个变量,因此存在变量内容被破坏的潜在可能。本地和自动变量被创建在线程的堆栈中,因而变量被破坏的可能性很小。堆栈的分配是从上至下的。例如,一个地址空间从0x08000000到0x080FF000的堆栈的分配是从0x080FF000到0x08001000来提交内存页,如果访问在0x08001000的页就会导致堆栈溢出异常。堆栈不能增长,任何试图访问堆栈以外的地址的操作都可能会导致进程被中止的致命错误。

2.4.4.会话内存的分配

当监听创建了一个用户会话(线程和堆栈)时,Oracle服务进程就通过调用Win32 API函数创建用户连接所必须的内存结构,并且指定MEM_RESERVE | MEM_COMMIT标识,以保持和提交给线程私有的地址空间区域。

当调用了VirtualAlloc来在指定地址保留内存,它会返回一个虚拟地址空间到下一个64K大块(chunk,windows内存分配最小单位)的地址。Oracle调用VirtualAlloc时不指定地址,只传一个NULL在相应参数中,这会返回到下一个64K大块的地址。因此用户会话在分配PGA、UGA和CGA时同时也遵循64K的最小粒度,来提交倍数于这个粒度值的内存页。许多用户会话经常分配许多小于64K的内存,这就导致地址空间出现碎片,因为许多64K区只有两个页被提交。

一旦地址空间被用户会话占满了后,如果要再创建一个新会话,就会存在无法分配到地址空间的危险。将可能报以下错误:

 ORA-12500 / TNS-12500

 TNS:listener failed to start a dedicatedserver process

也可能报这些错误:

o       ORA-12540 / TNS-12540 TNS:internal limit restriction exceeded

o       NT-8 Not enough storage is available to process this command

o       skgpspawn failed:category = ....

o       ORA-27142 could not create new process

o       ORA-27143 OS system call failure

o       ORA-4030 out of process memory when trying to allocate ....

因为地址空间碎片问题和DLL被载入了oracle服务进程的地址空间,这些错误很可能发生再当Oracle进程占用大概1.6G~1.7G(可以通过任务管理器或TopShow查看)时。

2.4.4.1.会话内存大小设置

我们前面说了,一个进程的全部内存大小被限制在2G以内。因此,对于一个有许多用户同时访问的系统,要考虑这些会话总内存小于2G - SGA的大小。

下列参数会影响每个会话的内存大小(这些参数前面都有介绍):

o       bitmap_merge_area_size

o       create_bitmap_area_size

o       hash_area_size

o       open_cursors

o       sort_area_size (sort_area_retained_size)

在没有设置PGA_AGGREGATE_TARGE(这个参数能尽量但不一定使所有会话PGA之和在指定范围内)参数时,需要调整这些参数,以使所有会话占用内存与SGA之和小于2G。过多的使用PL/SQL结构体(如PL/SQLTABLE、ARRAY)也会导致会话内存增大。

2.4.4.2.ORASTACK修改线程堆栈大小

Oracle提供了ORASTACK工具让用户内修改Oracle执行程序创建会话、线程时的默认堆栈大小。当ORASTACK应用于一个可执行程序时,它会修改程序头部的、定义使用创建线程API函数所指定默认堆栈大小的二进制区,以修改默认堆栈的大小。没有必要去修改线程提交页数的默认值,因为它们是从堆栈中请求到的。当用户非常多时,通过减少每个创建在Oracle中会话堆栈大小,可以节省大量内存。比如,一个1000用户的系统,将堆栈从1M降为500K后,能节省出1000 *500K = 500M的地址空间。

在需要使用ORASTACK来降低现场堆栈大小时,你需要测试你的系统以保证新的堆栈大小能确保系统正常运行。如果堆栈大小被缩小到Oracle服务端所必须的堆栈大小以下,就会产生堆栈溢出错误,用户进程就失败(通常报ORA-3113错误),并且在alert log中不会有报错而且页不产生trace文件。Oracle一般不推荐将堆栈改到500K以下(尽管不少系统在300K时也能正常运行)。

ORASTACK必须修改所有能在oracle中创建线程的进程,使用语法如下:

  orastack  executable_name  new_stack_size_in_bytes

下面的例子将堆栈改为500K

  orastack oracle.exe  500000
  orastack tnslsnr.exe 500000
  orastack svrmgrl.exe 500000
  orastack sqlplus.exe 500000

在使用ORASTACK之前必须保证没有任何oracle进程正在运行。

此外,如果有程序在本地连接(没有通过SQL*NET)到Oracle,也要先停止(如在本地运行sqlplus连接了实例)。

2.4.4.3.会话内存如何释放、线程如何结束

当会话成功结束后,它会按用Win32 API函数VirtualFree来释放它的内存,调用此函数时,需要指定MEM_DECOMMIT | MEM_RELEASE标识。当所有内存被释放后,堆栈也被释放,将Oracle进程中指向完成的会话的地址空间空闲出来。

如果一个用户会话被异常结束,它将不会释放它所分配的内存,这些内存页会被继续保留在Oracle进程的地址空间中,直到进程结束。会话的异常结束可能由以下原因导致的:

oShutdown abort.

oAlter session kill session.

oorakill杀掉的会话.

o  Oracle管理助手for Windows:kill session.

o其他杀线程的工具(TopShow工具提供了杀线程的功能)

Oracle建议尽量少使用以上方式(命令),特别是shutdown abort(这种停止实例的方法所带来的问题还不止这个)。当调用shutdown abort时,Oracle会调用Win32 API函数TerminateThread来中止每个用户会话,这个命令将直接杀掉线程而不释放它们的内存。如果系统已经接近2G地址空间的限制,Oracle实例再次启动要分配内存时就会产生问题。唯一释放Oracle全部内存的方法是停止和启动Oracle服务(服务OracleService<SID>)。

 

如果windows NT下的系统需要能被许多用户访问,可以通过以下措施来优化内存的使用:

o降低相关的PGA、UGA内存参数(如SORT_AREA_SIZE);

o降低SGA的参数;

o使数据库作业队列数(参数job_queue_processes控制)和并行查询slave(参数parallel_max_servers控制)最小,因为它们也会导致Oracle进程创建线程;

o使用ORASTACK降低会话、线程堆栈大小到500K;

o考虑使用MTS模式;

o考虑将Windows NT升级到Windows NT企业版;

o考虑升级硬件以支持Intel ESMA(Extended. ServerMemory Architecture,扩展服务内存架构)

0 0
原创粉丝点击