Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetOracleDecimal Memory Leak

@GilShalit posted this comment a year ago:

"Well, we have come to distrust ODP (.Net 2.0) after fighting a memory leak (in code we supplied to a customer) in GetOracleDecimal for over a year... Good luck!" – GilShalit Aug 27 '09 at 12:44

How did you solve it?

We have a service that queries an Oracle database every few minutes that is not releasing memory; after some investigation using WinDbg I discovered that this type is piling up in the finalize queue: Oracle.DataAccess.Types.OpoDecCtx.

Here's the line that I think is the problem:

decimal volume = (decimal)OracleDecimal.SetPrecision(reader.GetOracleDecimal(5), 28);

I commented this out and the memory leak disappeared.

Any thoughts will be appreciated - thanks!

like image 507
Tim Avatar asked Aug 04 '10 16:08

Tim


1 Answers

This is an old issue with ODP.NET (see here: Memory Problems with ODP.NET 10.1.0.4 ).

The OracleDecimal type holds a reference to an instance of an internal class named OpoDecCtx. OpoDecCtx implements IDisposable (as it's itself referencing unmanaged memory), but since OracleDecimal does not implement IDisposable, you'll have to wait for the garbage collector to run to free the underlying unmanaged memory. You can check all this using a tool such as .NET Reflector.

Although it's not technically a "physical" memory leak (memory will be eventually freed), it is actually a problem when you're dealing with a large amount of instances of the OracleDecimal type. I don't know why Oracle does not simply implement IDisposable, it's a simple thing to do...

Anyway, I suggest you do some hack job yourself, using reflection:

public static class OracleExtentions
{
    public static void Dispose(this OracleDecimal od) // build an extension method
    {
        if (OracleDecimalOpoDecCtx == null)
        {
            // cache the data
            // get the underlying internal field info
            OracleDecimalOpoDecCtx = typeof(OracleDecimal).GetField("m_opoDecCtx", BindingFlags.Instance | BindingFlags.NonPublic);
        }
        IDisposable disposable = OracleDecimalOpoDecCtx.GetValue(od) as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

    private static FieldInfo OracleDecimalOpoDecCtx;
}

And you would use it like this:

OracleDecimal od = reader.GetOracleDecimal(5);
decimal volume = (decimal)OracleDecimal.SetPrecision(od, 28);
od.Dispose();
like image 129
Simon Mourier Avatar answered Nov 02 '22 13:11

Simon Mourier