Failed Shared Pool Memory Allocation for LOB Inserts

ORA-04031: KTSL subheap, ktsl_load_disp-2

LOB inserts have been occasionally failing on both 19c and 21c databases:

ORA-04031: unable to allocate 52832 bytes of shared memory (shared pool,unknown object,KTSL subheap,ktsl_load_disp-2)

We’ve observed following:

  • The affected LOBs were securefiles.
  • The affected databases had 32k block size.
  • The size of shared pool allocation requests was around 52k.
  • The allocation attempts were on KTSL subheap with the comment ktsl_load_disp-2.

Free space

Surprisingly, there was enough free space in the shared pool (our shared pool constists of only one subpool):

==============================================
Memory Utilization of Subpool 1 (SGA HEAP)
==============================================
      Allocation Name                 Size         Max Size   Chunks
___________________________       _____________   __________  ______
"free memory                    "     550418056    588099904  186502

There was enough free space, but there weren’t any free sufficiently large chunks in the duration 1 to fulfill the memory allocation request:

select KSMCHDUR,KSMCHCOM,KSMCHCLS, count(*)
  from X$KSMSP 
  where 
	KSMCHSIZ >= 52832 
	and KSMCHCLS in ('free', 'R-free')
  group by KSMCHDUR,KSMCHCOM,KSMCHCLS ;

  KSMCHDUR KSMCHCOM         KSMCHCLS   COUNT(*)
---------- ---------------- -------- ----------
         4 free memory      free             73
         4 free memory      R-free           21
         1 free memory      R-free           23

Interestingly, reserved shared pool had 23 chunks in the duration 1 that could have satisfied the allocation request, but the allocation rather failed instead of taking a chunk from the reserved shared pool.

Tracing memory allocations with debugger

The Oracle server process allocates ktsl dispenser chunks (“ktsl_load_disp”) when creating a new LOB extent. These allocations can be tracked with the following gdb breakpoint:

b kghalo if $_regex((char *)(*(uint64_t *)($rsp+8+8+8)),"^ktsl_load_disp-2$")

commands 1
printf "kghalo - size: %d  comment: %s \n", $rdx, (char *)(*(uint64_t *)($rsp+8+8+8))
end

A short description of the elements in the breakpoint command above:

  • kghalo is one of Oracle C functions that allocate memory.
  • kghalo receives the allocation size through the 4th argument. According to x86 calling conventions, the 4th parameter is passed through the CPU register rdx.
  • The comment for the chunk, a.k.a allocation reason, is passed on the stack. Consequently, it is dereferenced via the stack pointer rsp.

For a longer explanation on how to trace function arguments with debugger read this .

The test table contains a CLOB column:

create user c##u no authentication default tablespace users;
grant create table to c##u ;
alter user c##u quota unlimited on users ;

create table c##u.t (c clob);

Attach with gdb to the Oracle server process before running insert:

attach PID

Run insert in the debugged session:

insert into c##u.t values ( rpad('a',32767,'a') );

You should get the following output on breakpoint on a database with the 32k block size:

kghalo - size: 52800  comment: ktsl_load_disp-2

Notice that the allocation request size of 52800 bytes is similar to the value we’ve got in the ORA-04031 message.

I’m displaying free chunks larger than 52800 bytes in another SQLPlus session:

  KSMCHDUR KSMCHCOM         KSMCHCLS   COUNT(*)
---------- ---------------- -------- ----------
         4 free memory      R-free            7
         4 free memory      free              1
         1 free memory      R-free           20
         1 free memory      free              2

On kghalo start, we have two free chunks in the duration 1 (excluding the reserved shared pool).

Now go to debugger and continue the execution until exiting kghalo:

fin

One free chunk in the duration 1 is gone after kghalo ended:

  KSMCHDUR KSMCHCOM         KSMCHCLS   COUNT(*)
---------- ---------------- -------- ----------
         4 free memory      R-free            7
         4 free memory      free              1
         1 free memory      R-free           20
         1 free memory      free              1

The same allocation in the database with 8k block size is exactly 4 times smaller (13200 bytes):

kghalo - size: 13200  comment: ktsl_load_disp-2

In conclusion, the allocation size is proportional to the block size. Therefore, databases with larger block sizes are more likely to hit the problem.

Tracing LOB inserts with an undocumented trace event

In the course of this troubleshooting, I discovered the undocumented trace event 45050 for tracing LOB inserts. I set all the flags in the bitmap to get the maximum verbosity:

ALTER SESSION SET EVENTS='45050 trace name context forever, level 4294967295' ;

The trace reveals the reason for increased memory allocation for 32k block size – memory per dispenser partition is proportional to the block size:

grep Memory DB8K_ora_2594545_0002.trc
Memory per disp partition is 150
grep Memory DB32K_ora_2580289_0001.trc
Memory per disp partition is 600

Summary

In summary, LOB insert allocates chunks for LOB dispensers when creating a new extent. The session gets ORA-04031 on failed memory allocation. The memory allocation can fail even when there’s enough free memory in the shared pool – in particular, when there’s a lack of contiguous space large enough to fulfill the allocation request. Since the requested allocation size is proportional to the block size, the error is more likely on databases with larger block sizes.

In most cases, you should use the default 8k block size anyway – it’s mostly used and tested. A non-default block size is a road less travelled and might lead to a problem unknown to Oracle. For example, here’s another side effect with larger block sizes.

Thanks for sharing

Nenad Noveljic

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.