|From: James J Greensky <jame.j.greensky@intel.com>

|Date: Wed, 5 Aug 2009 09:51:52 -0700
|Subject: [PATCH] Closed two major memory leaks for scripted objects
|
|Two major memory leaks for the scripted objects were fixed
|- One leak had to do with remoting acrossing app domains.  When a script and
|  its controlling agent communicate across an application boundary, it calls
|  functions on a stub proxy object that then invokes the remote method on
|  the object in the other app domain. These stub objects (two for each script)
|  were setup to have infinate lifetimes and were never being garbage collected.
|- The second leak was the result of adding a scene object part instance method
|  to a scene event and never removing it.  This cause the event's delegate list
|  to maintain a link to that object which is then never freed as the scene event
|  object is never destroyed.

Patch applied, please direct feedback to me. Possible issue: Longtime idle
scripts like vendors may fail.
arthursv
Melanie 2009-08-06 22:03:20 +01:00
parent eb9d584ee0
commit 91f6898b26
11 changed files with 73 additions and 68 deletions

View File

@ -953,11 +953,12 @@ namespace OpenSim.Region.Framework.Scenes
// this lets us keep track of nasty script events like timer, etc. // this lets us keep track of nasty script events like timer, etc.
public void TriggerTimerEvent(uint objLocalID, double Interval) public void TriggerTimerEvent(uint objLocalID, double Interval)
{ {
handlerScriptTimerEvent = OnScriptTimerEvent; throw new NotImplementedException("TriggerTimerEvent was thought to be not used anymore and the registration for the event from scene object part has been commented out due to a memory leak");
if (handlerScriptTimerEvent != null) //handlerScriptTimerEvent = OnScriptTimerEvent;
{ //if (handlerScriptTimerEvent != null)
handlerScriptTimerEvent(objLocalID, Interval); //{
} // handlerScriptTimerEvent(objLocalID, Interval);
//}
} }
/// <summary> /// <summary>

View File

@ -3673,14 +3673,14 @@ if (m_shape != null) {
return; return;
} }
if ((GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0) //if ((GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0)
{ //{
m_parentGroup.Scene.EventManager.OnScriptTimerEvent += handleTimerAccounting; // m_parentGroup.Scene.EventManager.OnScriptTimerEvent += handleTimerAccounting;
} //}
else //else
{ //{
m_parentGroup.Scene.EventManager.OnScriptTimerEvent -= handleTimerAccounting; // m_parentGroup.Scene.EventManager.OnScriptTimerEvent -= handleTimerAccounting;
} //}
LocalFlags=(PrimFlags)objectflagupdate; LocalFlags=(PrimFlags)objectflagupdate;

View File

@ -41,5 +41,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
Dictionary<string,Object> GetVars(); Dictionary<string,Object> GetVars();
void SetVars(Dictionary<string,Object> vars); void SetVars(Dictionary<string,Object> vars);
void ResetVars(); void ResetVars();
void Close();
} }
} }

View File

@ -119,14 +119,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
AsyncCommands = new AsyncCommandManager(ScriptEngine); AsyncCommands = new AsyncCommandManager(ScriptEngine);
} }
// Object never expires
public override Object InitializeLifetimeService() public override Object InitializeLifetimeService()
{ {
ILease lease = (ILease)base.InitializeLifetimeService(); ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial) if (lease.CurrentState == LeaseState.Initial)
{ {
lease.InitialLeaseTime = TimeSpan.Zero; lease.InitialLeaseTime = TimeSpan.FromMinutes(1.0);
lease.RenewOnCallTime = TimeSpan.FromSeconds(10.0);
lease.SponsorshipTimeout = TimeSpan.FromMinutes(1.0);
} }
return lease; return lease;
} }

View File

@ -159,16 +159,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
} }
//
// Never expire this object
//
public override Object InitializeLifetimeService() public override Object InitializeLifetimeService()
{ {
ILease lease = (ILease)base.InitializeLifetimeService(); ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial) if (lease.CurrentState == LeaseState.Initial)
{ {
lease.InitialLeaseTime = TimeSpan.Zero; lease.InitialLeaseTime = TimeSpan.FromMinutes(1.0);
lease.RenewOnCallTime = TimeSpan.FromSeconds(10.0);
lease.SponsorshipTimeout = TimeSpan.FromMinutes(1.0);
} }
return lease; return lease;
} }

View File

@ -35,7 +35,7 @@ using log4net;
namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
{ {
public class Executor : MarshalByRefObject public class Executor
{ {
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@ -89,26 +89,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
initEventFlags(); initEventFlags();
} }
/// <summary>
/// Make sure our object does not timeout when in AppDomain. (Called by ILease base class)
/// </summary>
/// <returns></returns>
public override Object InitializeLifetimeService()
{
//m_log.Debug("Executor: InitializeLifetimeService()");
// return null;
ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial)
{
lease.InitialLeaseTime = TimeSpan.Zero; // TimeSpan.FromMinutes(1);
// lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
// lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
}
return lease;
}
public scriptEvents GetStateEventFlags(string state) public scriptEvents GetStateEventFlags(string state)
{ {
//m_log.Debug("Get event flags for " + state); //m_log.Debug("Get event flags for " + state);

View File

@ -32,7 +32,7 @@ using LSLInteger = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
{ {
public partial class ScriptBaseClass : MarshalByRefObject public partial class ScriptBaseClass
{ {
// LSL CONSTANTS // LSL CONSTANTS
public static readonly LSLInteger TRUE = new LSLInteger(1); public static readonly LSLInteger TRUE = new LSLInteger(1);

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Lifetime; using System.Runtime.Remoting.Lifetime;
using System.Security.Permissions; using System.Security.Permissions;
using System.Threading; using System.Threading;
@ -34,26 +35,23 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using OpenSim.Region.ScriptEngine.Interfaces; using OpenSim.Region.ScriptEngine.Interfaces;
using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared;
using OpenSim.Region.ScriptEngine.Shared.Api.Runtime;
namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
{ {
public partial class ScriptBaseClass : MarshalByRefObject, IScript public partial class ScriptBaseClass : MarshalByRefObject, IScript
{ {
private Dictionary<string, MethodInfo> inits = new Dictionary<string, MethodInfo>(); private Dictionary<string, MethodInfo> inits = new Dictionary<string, MethodInfo>();
private ScriptSponsor m_sponser;
// Object expires if we don't keep it alive
// sponsor will be added on object load
[SecurityPermissionAttribute(SecurityAction.Demand,
Flags = SecurityPermissionFlag.Infrastructure)]
public override Object InitializeLifetimeService() public override Object InitializeLifetimeService()
{ {
ILease lease = (ILease)base.InitializeLifetimeService(); ILease lease = (ILease)base.InitializeLifetimeService();
if (lease.CurrentState == LeaseState.Initial) if (lease.CurrentState == LeaseState.Initial)
{ {
lease.InitialLeaseTime = TimeSpan.Zero; lease.InitialLeaseTime = TimeSpan.FromMinutes(1.0);
// lease.InitialLeaseTime = TimeSpan.FromMinutes(1); lease.RenewOnCallTime = TimeSpan.FromSeconds(10.0);
// lease.SponsorshipTimeout = TimeSpan.FromMinutes(2); lease.SponsorshipTimeout = TimeSpan.FromMinutes(1.0);
// lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
} }
return lease; return lease;
} }
@ -66,7 +64,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
} }
#endif #endif
public ScriptBaseClass() public ScriptBaseClass()
{ {
m_Executor = new Executor(this); m_Executor = new Executor(this);
@ -81,6 +78,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
inits[type] = mi; inits[type] = mi;
} }
} }
m_sponser = new ScriptSponsor();
} }
private Executor m_Executor = null; private Executor m_Executor = null;
@ -112,6 +111,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
if (!inits.ContainsKey(api)) if (!inits.ContainsKey(api))
return; return;
ILease lease = (ILease)RemotingServices.GetLifetimeService(data as MarshalByRefObject);
lease.Register(m_sponser);
MethodInfo mi = inits[api]; MethodInfo mi = inits[api];
Object[] args = new Object[1]; Object[] args = new Object[1];
@ -122,6 +124,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
m_InitialValues = GetVars(); m_InitialValues = GetVars();
} }
public void Close()
{
m_sponser.Close();
}
public Dictionary<string, object> GetVars() public Dictionary<string, object> GetVars()
{ {
Dictionary<string, object> vars = new Dictionary<string, object>(); Dictionary<string, object> vars = new Dictionary<string, object>();

View File

@ -32,15 +32,19 @@ using System.Text;
namespace OpenSim.Region.ScriptEngine.Shared.Api.Runtime namespace OpenSim.Region.ScriptEngine.Shared.Api.Runtime
{ {
[Serializable]
public class ScriptSponsor : MarshalByRefObject, ISponsor public class ScriptSponsor : MarshalByRefObject, ISponsor
{ {
// In theory: I execute, therefore I am. private bool m_closed = false;
// If GC collects this class then sponsorship will expire
public TimeSpan Renewal(ILease lease) public TimeSpan Renewal(ILease lease)
{ {
return TimeSpan.FromMinutes(2); if (!m_closed)
return lease.InitialLeaseTime;
return TimeSpan.FromTicks(0);
} }
public void Close() { m_closed = true; }
#if DEBUG #if DEBUG
// For tracing GC while debugging // For tracing GC while debugging
public static bool GCDummy = false; public static bool GCDummy = false;

View File

@ -96,7 +96,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
private string m_CurrentState = String.Empty; private string m_CurrentState = String.Empty;
private UUID m_RegionID = UUID.Zero; private UUID m_RegionID = UUID.Zero;
//private ISponsor m_ScriptSponsor; private ScriptSponsor m_ScriptSponsor;
private bool m_destroyed = false;
private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> private Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>>
m_LineMap; m_LineMap;
@ -261,12 +262,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
Path.GetFileNameWithoutExtension(assembly), Path.GetFileNameWithoutExtension(assembly),
"SecondLife.Script"); "SecondLife.Script");
// Add a sponsor to the script m_ScriptSponsor = new ScriptSponsor();
// ISponsor scriptSponsor = new ScriptSponsor(); ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass);
// ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as MarshalByRefObject); lease.Register(m_ScriptSponsor);
// lease.Register(scriptSponsor);
//m_ScriptSponsor = scriptSponsor;
} }
catch (Exception) catch (Exception)
{ {
@ -449,6 +447,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
{ {
ReleaseControls(); ReleaseControls();
AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID); AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
m_Script.Close();
m_ScriptSponsor.Close();
ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass);
lease.Unregister(m_ScriptSponsor);
m_destroyed = true;
} }
public void RemoveState() public void RemoveState()
@ -884,6 +889,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public void SaveState(string assembly) public void SaveState(string assembly)
{ {
// If we're currently in an event, just tell it to save upon return // If we're currently in an event, just tell it to save upon return
// //
if (m_InEvent) if (m_InEvent)
@ -892,6 +899,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
return; return;
} }
// Data may not be available as the script has already been destroyed
if (m_destroyed == true)
return;
PluginData = AsyncCommandManager.GetSerializationData(m_Engine, m_ItemID); PluginData = AsyncCommandManager.GetSerializationData(m_Engine, m_ItemID);
string xml = ScriptSerializer.Serialize(this); string xml = ScriptSerializer.Serialize(this);

View File

@ -272,6 +272,10 @@ namespace OpenSim.Region.ScriptEngine.XEngine
instance.ClearQueue(); instance.ClearQueue();
instance.Stop(0); instance.Stop(0);
// Release events, timer, etc
//
instance.DestroyScriptInstance();
// Unload scripts and app domains // Unload scripts and app domains
// Must be done explicitly because they have infinite // Must be done explicitly because they have infinite
// lifetime // lifetime
@ -282,10 +286,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
m_DomainScripts.Remove(instance.AppDomain); m_DomainScripts.Remove(instance.AppDomain);
UnloadAppDomain(instance.AppDomain); UnloadAppDomain(instance.AppDomain);
} }
// Release events, timer, etc
//
instance.DestroyScriptInstance();
} }
m_Scripts.Clear(); m_Scripts.Clear();
m_PrimObjects.Clear(); m_PrimObjects.Clear();
@ -802,6 +802,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
} }
} }
instance.RemoveState();
instance.DestroyScriptInstance();
m_DomainScripts[instance.AppDomain].Remove(instance.ItemID); m_DomainScripts[instance.AppDomain].Remove(instance.ItemID);
if (m_DomainScripts[instance.AppDomain].Count == 0) if (m_DomainScripts[instance.AppDomain].Count == 0)
{ {
@ -809,9 +812,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
UnloadAppDomain(instance.AppDomain); UnloadAppDomain(instance.AppDomain);
} }
instance.RemoveState();
instance.DestroyScriptInstance();
instance = null; instance = null;
ObjectRemoved handlerObjectRemoved = OnObjectRemoved; ObjectRemoved handlerObjectRemoved = OnObjectRemoved;