Recent Posts

RSS Feeds

DBA日记 第一部(30) (补) ORA-4030和内存泄露

这几天在整理DBA日记所以很久没有更新了。这是补充6月1日的日记,并且在日记里增加了每日一技,会针对当天遇到的问题,介绍一些技术和工作技巧。以前各个章节的每日一技就不补充了,等以后一起发布吧。

6月1日 ORA-4030和内存泄露

一大早过来就看了看昨天晚上录制的数据,快进了半天也没看到什么特殊的东西,干脆不看了,打电话问了几个昨天晚上做账务处理的人,大家都说比较正常,除了速度比上个月更慢了一些以外,没有性能下降特别多的模块。账务处理的速度每个月都是线性下降的,所以大家都觉得很正常,并没有什么特别的地方。既然大家都没觉得什么,昨天晚上录制的东西也就懒得去看了。不过看录像的时候突然发现这台安装OMS服务器的微机速度比我那台借来的老掉牙的笔记本快多了,干脆用这台电脑工作吧,用那台电脑起码降低1/3的工作效率。

由于优化的方案基本上都确定了,剩下的事情老于他们在做细化。SQL优化的工作有老熊帮忙,我也轻松了许多。前面几批提交的3、40个SQL的优化方案也分析的差不多了,大部分都已经由张工转发给了开发商。由于昨天电脑故障,今天我主要的工作还是在现有的电脑上安装相关的工具,拷贝相关的文件。

这几天系统虽然负载很重,不过这种情况已经持续了快一年了,所有的人也都没有感觉什么不适应的地方。中午快吃午饭的时候,张工突然打电话过来,说他们那边有一个数据库出现了ORA-4030错误。由于那套系统并不在我们合同范围之内,因此张工很委婉的问我有没有时间,帮她们看看。使用张工给我的账号密码登录到了系统里。发现报错的信息是:

ORA-04030: out of process memory when trying to allocate 2097192 bytes (joxcx callheap,ioc_allocate ufree)

Current SQL statement for this session:

call CRM_OPER.oper(:1,:2)

----- PL/SQL Call Stack -----

object line object

handle number name

c000000100da8108 0 package SYS.XMLNODECOVER

c000000100dada80 381 package body SYS.XMLDOM

c000000103a1d178 77 package body CRM.CRM_OPER

看到这个错误信息,首先我查看了一下ulimit,发现ulimit设置很正常,没有可能导致故障的设置。检查物理内存的使用情况,发现物理内存还有一些空闲,虽然不是很多,不过也就300多兆。SWAP区的使用率相当高,高达98%。从这一点上可以看出,不久前系统曾经出现过十分严重的换页现象。从CALL STACK中我们可以看出,这是CRM.CRM_OPER在调用XMLDOM包的时候报错。DOM操作是十分消耗内存的,这回报错的是一次申请2M的内存空间。看到DOM操作我的第一个反应就是是不是DOM对象使用后没有释放。于是马上找到CRM_OPER的代码进行分析,有一段代码引起了我的注意:

parser := xmlparser.newParser;

xmlparser.parseBuffer(parser,operInfo);

doc := xmlparser.getDocument(parser);

xmlparser.freeParser(parser);

虽然xmlparser被释放了,而Documentation对象DOC并没有释放。Doc是一个dom对象,如果不释放,DOM对象所占的空间将会随着调用的增加而叠加,最终导致物理内存被耗尽。为了证实这个猜测,首先编写一个测试程序:

create or replace procedure oper is

parser xmlparser.Parser;

doc sys.xmldom.DOMDocument;

operInfo varchar2(2000);

i integer;

begin

operInfo:='<?xml version="1.0" standalone="yes" ?>';

operInfo:=operInfo||'<QUERY_LIST>';

operInfo:=operInfo||'<LIB NAME="areasQueries" VER="2.2.0.7.0" REP_VER="0.0" MIN_INST_VER="2.1.0.4.1" INV_LOC="Queries21/areasQueries/2.2.0.7.0/areasQueries.jar"/>';

operInfo:=operInfo||'</QUERY_LIST>';

i:=0;

parser := xmlparser.newParser;

xmlparser.parseBuffer(parser,operInfo);

doc := xmlparser.getDocument(parser);

xmlparser.freeParser(parser);

-- xmldom.freeDocument(doc);

i:=i+1;

end;

/

分别进行2次测试,第一次不执行xmldom.freDocument(doc),第二次执行该语句。通过下面的方法调用:

declare

i integer;

begin

i:=0;

loop

oper;

i:=i+1;

exit when i>100;

end loop;

end;

/

每调用一次,通过下列语句检查PGA的情况:

select value, n.name|| '('||s.statistic#||')'

from v$sesstat s , v$statname n

where s.statistic# = n.statistic#

and n.name like '%ga memory%'

and sid= <sid of problem session>;

首先进行一次调用freeDocument的测试,查看UGA和PGA的使用情况。在调用前,结果是:

-------------------------------------

142476 session uga memory(15)

207940 session uga memory max(16)

710024 session pga memory(20)

1037704 session pga memory max(21)

第一次执行后的情况:

-------------------------------------

207940 session uga memory(15)

273404 session uga memory max(16)

8165056 session pga memory(20)

8165056 session pga memory max(21)

第二次执行后的情况:

-------------------------------------

207940 session uga memory(15)

273404 session uga memory max(16)

7882672 session pga memory(20)

10734088 session pga memory max(21)

可以看出随着调用次数的增长,PGA和UGA并没有明显的增长。下面修改一下测试程序,将freeDocument的部分去掉。没执行的时候PGA情况:

-------------------------------------

77012 session uga memory(15)

142476 session uga memory max(16)

776488 session pga memory(20)

842024 session pga memory max(21)

执行第一次后的情况:

-------------------------------------

142476 session uga memory(15)

207940 session uga memory max(16)

5188024 session pga memory(20)

5253560 session pga memory max(21)

执行第二次后的情况:

-------------------------------------

142476 session uga memory(15)

207940 session uga memory max(16)

5126608 session pga memory(20)

5253560 session pga memory max(21)

通过WINDOWS任务管理器观察ORACLE进程的物理内存情况可以得出类似的结果。就是如果释放DOM对象,PGA内存不会持续增长,如果不释放DOM对象,PGA内存会越占越多。通过WINDOWS任务管理器可以看出,执行200次存储过程,ORACLE进程的内存使用增加了200M。从上述测试中可以得出结论,由于没有释放DOCUMENT对象,导致了内存泄露。我把这个结果告诉了张工,他和开发人员确认了一下,确认这个模块是昨天下午才上线的一个应用调用的,程序员也确认不释放DOCUMENT对象是错误的。

处理完这个小故障,已经是中午的12点半了。办公室里除了老于其他人都去吃饭了。我急忙锁了屏幕,和老于下楼去吃饭。中午吃饭的时候,老于一直比较沉默,我问他是不是有什么心事。他说有点想家,老于家就是锦州的,原计划这个周末回家休息一下,由于6号要做实施,这个计划只能泡汤了。做DBA的,很多象老于那样,一年到头没有多长时间能够和家里人团聚。

0.1.1. 每日一技 如何分析ORA-4030

ORA-4030一般来说是进程从操作系统申请内存的时候,操作系统无法分配内存引起的。一般来说ORA-4030可能是几方面原因引起:操作系统无法从物理内存和SWAP区分配内存给进程,或者进程受到了操作系统的某个限制条件的约束无法分配更多内存。还有可能就是碰到了某个ORACLE的BUG。

碰到出现ORA-4030,首先我们需要检查的是操作系统的物理内存和SWAP区是否满了。如果物理内存所剩无几,并且SWAP区接近或者达到100%,那么就是真正的内存不足了。一般情况下这种现象出现的较少,大多数ORA-4030是由于进程或者操作系统参数限制引起。

如果我们发现物理内存还有很多剩余,那么下一步需要检查的是oracle账户的ulimit参数。比如:

SUN OS:

/tmp/oralog>>> ulimit -a

time(seconds) unlimited

file(blocks) unlimited

data(kbytes) unlimited

stack(kbytes) 8192

coredump(blocks) unlimited

nofiles(descriptors) 256

vmemory(kbytes) unlimited

HP-UX:

$[/oracle]ulimit -a

time(seconds) unlimited

file(blocks) unlimited

data(kbytes) 1048576

stack(kbytes) 131072

memory(kbytes) unlimited

coredump(blocks) 4194303

nofiles(descriptors) 2048

AIX:

[/home/oracle9i]#ulimit -a

time(seconds) unlimited

file(blocks) unlimited

data(kbytes) unlimited

stack(kbytes) 65536

memory(kbytes) unlimited

coredump(blocks) 2097151

nofiles(descriptors) unlimited

其中的DATA,STACK,MEMORY等参数过小都可能引起ORA-4030。一般来说这些参数建议设置为-1(unlimited)。如果ulimit参数没有问题,那么也可能是其他操作系统内核参数引起。我们可以使用下面一个程序去测试一下,一个进程能够分配到的物理内存的大小:

/* mem.c */
#include <stdio.h>
#include <errno.h>
main()
{
long int i;
i = 1;
for(;;)
{
if (!malloc(1048576))
{
printf("malloc failed with error: %u at iteration %u\n", errno,i);
exit(-1);
Permalink     No Comments



发表一条评论:
  • HTML语法: 启用