Hi!
I recently ran into a memory leakage like problem when using NHibernate in a windows service. My guess is that the SessionFactory is not releasing some resources under certain circumstances. I've written some code to reproduce the problem. When running this testapp as a console application, the problem does not appear, when running it as a nunit test, it does. A mystery for me...
NHibernateMemoryLeakage.MemoryLeakage() opens a session, gets a customer object from the Northwind database and disposes of the session. It does this a thousand times. The result from GC.GetTotalMemory(true) keeps growing.
NHibernateMemoryLeakage.MemoryLeakageDisposeSessionFactory() does the same thing, but also disposes of SessionFactory each iteration. The result from GC.GetTotalMemory(true) does not keep growing.
The memory leakage problem in my windows service disappearad when I also disposed of the SessionFactory whenever disposing of a Session.
My system configuration for this test:
.NET2.0 (version 2.0.50727.42)
Windows XP SP2
NHibernate 1.2.0.4000 (Build 1.2.0.GA)
NUnit 2.2.9.0
App.config:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section
name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"
/>
</configSections>
<system.diagnostics>
<trace autoflush="true">
<listeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener" />
</listeners>
</trace>
</system.diagnostics>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Data Source=(local);Initial Catalog=Northwind;Integrated Security=True</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<mapping assembly="NHibernateMemoryLeakage"/>
</session-factory>
</hibernate-configuration>
</configuration>
Customer.cs
Code:
using System;
namespace NHibernateMemoryLeakage
{
public class Customer
{
private string id;
public virtual string Id
{
get { return id; }
set { id = value; }
}
private string companyName;
public virtual string CompanyName
{
get { return companyName; }
set { companyName = value; }
}
private string contactName;
public virtual string ContactName
{
get { return contactName; }
set { contactName = value; }
}
private string contactTitle;
public virtual string ContactTitle
{
get { return contactTitle; }
set { contactTitle = value; }
}
private string address;
public virtual string Address
{
get { return address; }
set { address = value; }
}
private string city;
public virtual string City
{
get { return city; }
set { city = value; }
}
private string region;
public virtual string Region
{
get { return region; }
set { region = value; }
}
private string postalCode;
public virtual string PostalCode
{
get { return postalCode; }
set { postalCode = value; }
}
private string country;
public virtual string Country
{
get { return country; }
set { country = value; }
}
private string phone;
public virtual string Phone
{
get { return phone; }
set { phone = value; }
}
private string fax;
public virtual string Fax
{
get { return fax; }
set { fax = value; }
}
}
}
Customer.hbm.xml
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateMemoryLeakage" namespace="NHibernateMemoryLeakage">
<class name="Customer" table="Customers" lazy="true">
<id column="CustomerID" name="Id">
<generator class="assigned" />
</id>
<property name="CompanyName" not-null="true" />
<property name="ContactName" not-null="false" />
<property name="ContactTitle" not-null="false" />
<property name="Address" not-null="false" />
<property name="City" not-null="false" />
<property name="Region" not-null="false" />
<property name="PostalCode" not-null="false" />
<property name="Country" not-null="false" />
<property name="Phone" not-null="false" />
<property name="Fax" not-null="false" />
</class>
</hibernate-mapping>
NHibernateTest.cs:
Code:
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using System.Diagnostics;
namespace NHibernateMemoryLeakage
{
[TestFixture]
public class NHibernateTest
{
private long lastPrivateMemory;
static void Main(string[] args)
{
new NHibernateTest().MemoryLeakage();
}
[Test]
public void MemoryLeakage()
{
lastPrivateMemory = 0;
for (int i = 0; i < 1000; i++)
getCustomer();
}
[Test]
public void MemoryLeakageDisposeSessionFactory()
{
lastPrivateMemory = 0;
for (int i = 0; i < 1000; i++)
{
getCustomer();
sessionFactory.Dispose();
sessionFactory = null;
}
}
private void getCustomer()
{
using (NHibernate.ISession session = SessionFactory.OpenSession())
{
Customer customer = session.Get<Customer>("ALFKI");
long currentPrivateMemory = GC.GetTotalMemory(true);
long diff = currentPrivateMemory - lastPrivateMemory;
Trace.WriteLine(String.Format("Current: {0}, Diff: {1}", currentPrivateMemory, diff));
lastPrivateMemory = currentPrivateMemory;
}
}
private NHibernate.ISessionFactory sessionFactory = null;
public NHibernate.ISessionFactory SessionFactory
{
get
{
if (sessionFactory == null)
sessionFactory = new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
return sessionFactory;
}
}
}
}
Thanks,
Peter Albertsson