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