Skip to content

Commit be60642

Browse files
montagutcarloshazzik
authored andcommitted
NH-3046 - Fix potential memory leak
1 parent 4af4a5e commit be60642

File tree

7 files changed

+132
-13
lines changed

7 files changed

+132
-13
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
2+
using System;
3+
using NUnit.Framework;
4+
using NHibernate.Mapping.ByCode;
5+
using NHibernate.Cfg;
6+
7+
namespace NHibernate.Test.NHSpecificTest.NH3046
8+
{
9+
[TestFixture]
10+
public class Fixture : BugTestCase
11+
{
12+
[Test]
13+
public void MemoryLeak()
14+
{
15+
16+
long initialMemory = GC.GetTotalMemory(true);
17+
long nextId = 1;
18+
long nextIdChild = 1;
19+
20+
using (ISession session = OpenSession())
21+
using (session.BeginTransaction())
22+
{
23+
// We try to insert 100.000 entities, cleaning the sessions
24+
// every 1000.
25+
// We keep track of memory, that should be increasing forever.
26+
// We took a maximum of 250000 that can change to greater number but
27+
// not increase forever.
28+
for (int i = 0; i < 100; i++)
29+
{
30+
GC.Collect();
31+
long currentMemory = GC.GetTotalMemory(true);
32+
long memoryIncrease = currentMemory - initialMemory;
33+
34+
Assert.Less(memoryIncrease, 400000);
35+
// Console.WriteLine(memoryIncrease);
36+
for (int j = 0; j < 1000; j++)
37+
{
38+
Parent a = new Parent();
39+
a.Id = nextId++;
40+
41+
Child c = new Child();
42+
c.Id = nextIdChild++;
43+
44+
a.Childs.Add(c);
45+
46+
session.Save(c);
47+
session.Save(a);
48+
}
49+
session.Flush();
50+
session.Clear();
51+
}
52+
}
53+
}
54+
55+
protected override string CacheConcurrencyStrategy
56+
{
57+
get { return null; }
58+
}
59+
}
60+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping
3+
xmlns="urn:nhibernate-mapping-2.2"
4+
assembly="NHibernate.Test"
5+
namespace="NHibernate.Test.NHSpecificTest.NH3046">
6+
7+
<class name="Parent">
8+
<id name="Id"/>
9+
10+
<bag name="Childs" lazy="true" inverse="true" cascade="all">
11+
<key column="parent_id" />
12+
<one-to-many class="Child" />
13+
</bag>
14+
15+
</class>
16+
17+
<class name="Child">
18+
<id name="Id"/>
19+
</class>
20+
21+
</hibernate-mapping>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH3046
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
6+
public class Parent
7+
{
8+
public Parent()
9+
{
10+
Childs = new List<Child>();
11+
}
12+
13+
public virtual long Id { get; set; }
14+
public virtual IList<Child> Childs { get; set; }
15+
}
16+
17+
public class Child
18+
{
19+
public virtual long Id { get; set; }
20+
}
21+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,8 @@
726726
<Compile Include="NHSpecificTest\NH2931\Fixture.cs" />
727727
<Compile Include="NHSpecificTest\NH2931\Mappings.cs" />
728728
<Compile Include="NHSpecificTest\NH2931\Models.cs" />
729+
<Compile Include="NHSpecificTest\NH3046\Fixture.cs" />
730+
<Compile Include="NHSpecificTest\NH3046\Model.cs" />
729731
<Compile Include="NHSpecificTest\NH3518\ClassWithXmlMember.cs" />
730732
<Compile Include="NHSpecificTest\NH3518\XmlColumnTest.cs" />
731733
<Compile Include="NHSpecificTest\NH3609\MappingEntity.cs" />
@@ -3162,6 +3164,7 @@
31623164
<EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" />
31633165
</ItemGroup>
31643166
<ItemGroup>
3167+
<EmbeddedResource Include="NHSpecificTest\NH3046\Mappings.hbm.xml" />
31653168
<EmbeddedResource Include="NHSpecificTest\NH3518\Mappings.hbm.xml" />
31663169
<EmbeddedResource Include="NHSpecificTest\NH3609\Mappings.hbm.xml" />
31673170
<EmbeddedResource Include="NHSpecificTest\NH3818\Mappings.hbm.xml" />

src/NHibernate/Action/CollectionAction.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,19 @@ public virtual BeforeTransactionCompletionProcessDelegate BeforeTransactionCompl
114114

115115
public virtual AfterTransactionCompletionProcessDelegate AfterTransactionCompletionProcess
116116
{
117+
117118
get
118119
{
119-
return new AfterTransactionCompletionProcessDelegate((success) =>
120+
// Only make sense to add the delegate if there is a cache.
121+
if (persister.HasCache)
120122
{
121-
if (persister.HasCache)
123+
return new AfterTransactionCompletionProcessDelegate((success) =>
122124
{
123-
CacheKey ck = Session.GenerateCacheKey(key, persister.KeyType, persister.Role);
125+
CacheKey ck = new CacheKey(key, persister.KeyType, persister.Role, Session.EntityMode, Session.Factory);
124126
persister.Cache.Release(ck, softLock);
125-
}
126-
});
127+
});
128+
}
129+
return null;
127130
}
128131
}
129132

src/NHibernate/Action/CollectionUpdateAction.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,11 @@ public override AfterTransactionCompletionProcessDelegate AfterTransactionComple
125125
{
126126
get
127127
{
128-
return new AfterTransactionCompletionProcessDelegate((success) =>
128+
// Only make sense to add the delegate if there is a cache.
129+
if (Persister.HasCache)
129130
{
130131
// NH Different behavior: to support unlocking collections from the cache.(r3260)
131-
if (Persister.HasCache)
132+
return new AfterTransactionCompletionProcessDelegate((success) =>
132133
{
133134
CacheKey ck = Session.GenerateCacheKey(Key, Persister.KeyType, Persister.Role);
134135

@@ -140,7 +141,7 @@ public override AfterTransactionCompletionProcessDelegate AfterTransactionComple
140141
{
141142
CollectionCacheEntry entry = new CollectionCacheEntry(Collection, Persister);
142143
bool put = Persister.Cache.AfterUpdate(ck, entry, null, Lock);
143-
144+
144145
if (put && Session.Factory.Statistics.IsStatisticsEnabled)
145146
{
146147
Session.Factory.StatisticsImplementor.SecondLevelCachePut(Persister.Cache.RegionName);
@@ -151,8 +152,9 @@ public override AfterTransactionCompletionProcessDelegate AfterTransactionComple
151152
{
152153
Persister.Cache.Release(ck, Lock);
153154
}
154-
}
155-
});
155+
});
156+
}
157+
return null;
156158
}
157159
}
158160
}

src/NHibernate/Action/EntityAction.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ public virtual BeforeTransactionCompletionProcessDelegate BeforeTransactionCompl
106106
{
107107
get
108108
{
109-
return new BeforeTransactionCompletionProcessDelegate(BeforeTransactionCompletionProcessImpl);
109+
return NeedsBeforeTransactionCompletion()
110+
? new BeforeTransactionCompletionProcessDelegate(BeforeTransactionCompletionProcessImpl)
111+
: null;
110112
}
111113
}
112114

@@ -120,11 +122,18 @@ public virtual AfterTransactionCompletionProcessDelegate AfterTransactionComplet
120122
}
121123
}
122124

123-
private bool NeedsAfterTransactionCompletion()
125+
protected virtual bool NeedsAfterTransactionCompletion()
124126
{
125127
return persister.HasCache || HasPostCommitEventListeners;
126128
}
127-
129+
130+
protected virtual bool NeedsBeforeTransactionCompletion()
131+
{
132+
// At the moment, there is no need to add the delegate,
133+
// Subclasses can override this method and add the delegate if needed.
134+
return false;
135+
}
136+
128137
protected virtual void BeforeTransactionCompletionProcessImpl()
129138
{
130139
}

0 commit comments

Comments
 (0)