Skip to content

Commit

Permalink
[feat]新增Gen2GcCallback,配合轻量级内存池,在GC2时完全释放池中对象。
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Sep 16, 2024
1 parent 5719fbb commit a36923a
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 125 deletions.
9 changes: 1 addition & 8 deletions NewLife.Core/Caching/Cache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,13 +376,11 @@ public virtual Int64 Bench(Boolean rand = false, Int32 batch = 0)
var times = GetTimesPerThread(rand, batch);

// 提前准备Keys,减少性能测试中的干扰
var key = "bstr_";
//var key2 = "bint_";
var key = "b_";
var max = cpu > 64 ? cpu : 64;
var maxTimes = times * max;
if (!rand) maxTimes = max;
_keys = new String[maxTimes];
//_keys2 = new String[maxTimes];

var sb = new StringBuilder();
for (var i = 0; i < _keys.Length; i++)
Expand All @@ -391,11 +389,6 @@ public virtual Int64 Bench(Boolean rand = false, Int32 batch = 0)
sb.Append(key);
sb.Append(i);
_keys[i] = sb.ToString();

//sb.Clear();
//sb.Append(key2);
//sb.Append(i);
//_keys2[i] = sb.ToString();
}

// 单线程
Expand Down
54 changes: 12 additions & 42 deletions NewLife.Core/Collections/IPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public interface IPool<T>

/// <summary>归还</summary>
/// <param name="value"></param>
[Obsolete("Please use Return from 2024-02-01")]
Boolean Put(T value);

/// <summary>归还</summary>
Expand All @@ -35,9 +36,6 @@ public interface IPool<T>
/// </remarks>
public static class Pool
{
#region 扩展
#endregion

#region StringBuilder
/// <summary>字符串构建器池</summary>
public static IPool<StringBuilder> StringBuilder { get; set; } = new StringBuilderPool();
Expand All @@ -53,7 +51,7 @@ public static String Put(this StringBuilder sb, Boolean requireResult = false)

var str = requireResult ? sb.ToString() : String.Empty;

Pool.StringBuilder.Put(sb);
Pool.StringBuilder.Return(sb);

return str;
}
Expand All @@ -68,7 +66,7 @@ public static String Return(this StringBuilder sb, Boolean returnResult = true)

var str = returnResult ? sb.ToString() : String.Empty;

Pool.StringBuilder.Put(sb);
Pool.StringBuilder.Return(sb);

return str;
}
Expand All @@ -82,22 +80,13 @@ public class StringBuilderPool : Pool<StringBuilder>
/// <summary>最大容量。超过该大小时不进入池内,默认4k</summary>
public Int32 MaximumCapacity { get; set; } = 4 * 1024;

/// <summary>实例化字符串池。GC2时回收</summary>
public StringBuilderPool() : base(0, true) { }

/// <summary>创建</summary>
/// <returns></returns>
protected override StringBuilder OnCreate() => new(InitialCapacity);

/// <summary>归还</summary>
/// <param name="value"></param>
/// <returns></returns>
public override Boolean Put(StringBuilder value)
{
if (value.Capacity > MaximumCapacity) return false;

value.Clear();

return base.Put(value);
}

/// <summary>归还</summary>
/// <param name="value"></param>
/// <returns></returns>
Expand All @@ -121,16 +110,7 @@ public override Boolean Return(StringBuilder value)
/// <param name="requireResult">是否需要返回结果</param>
/// <returns></returns>
[Obsolete("Please use Return from 2024-02-01")]
public static Byte[] Put(this MemoryStream ms, Boolean requireResult = false)
{
//if (ms == null) return null;

var buf = requireResult ? ms.ToArray() : Empty;

Pool.MemoryStream.Put(ms);

return buf;
}
public static Byte[] Put(this MemoryStream ms, Boolean requireResult = false) => Return(ms, requireResult);

/// <summary>归还一个内存流到对象池</summary>
/// <param name="ms"></param>
Expand All @@ -142,7 +122,7 @@ public static Byte[] Return(this MemoryStream ms, Boolean returnResult = true)

var buf = returnResult ? ms.ToArray() : Empty;

Pool.MemoryStream.Put(ms);
Pool.MemoryStream.Return(ms);

return buf;
}
Expand All @@ -156,23 +136,13 @@ public class MemoryStreamPool : Pool<MemoryStream>
/// <summary>最大容量。超过该大小时不进入池内,默认64k</summary>
public Int32 MaximumCapacity { get; set; } = 64 * 1024;

/// <summary>实例化字符串池。GC2时回收</summary>
public MemoryStreamPool() : base(0, true) { }

/// <summary>创建</summary>
/// <returns></returns>
protected override MemoryStream OnCreate() => new(InitialCapacity);

/// <summary>归还</summary>
/// <param name="value"></param>
/// <returns></returns>
public override Boolean Put(MemoryStream value)
{
if (value.Capacity > MaximumCapacity) return false;

value.Position = 0;
value.SetLength(0);

return base.Put(value);
}

/// <summary>归还</summary>
/// <param name="value"></param>
/// <returns></returns>
Expand All @@ -191,7 +161,7 @@ public override Boolean Return(MemoryStream value)
#region ByteArray
/// <summary>字节数组共享存储</summary>
public static ArrayPool<Byte> Shared { get; set; } = ArrayPool<Byte>.Shared;

/// <summary>空数组</summary>
public static Byte[] Empty { get; } = [];
#endregion
Expand Down
59 changes: 4 additions & 55 deletions NewLife.Core/Collections/ObjectPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,59 +183,8 @@ public virtual T Get()

/// <summary>归还</summary>
/// <param name="value"></param>
public virtual Boolean Put(T value)
{
if (value == null) return false;

// 从繁忙队列找到并移除缓存项
if (!_busy.TryRemove(value, out var pi))
{
#if DEBUG
WriteLog("Put Error");
#endif
Interlocked.Increment(ref _ReleaseCount);

return false;
}

Interlocked.Decrement(ref _BusyCount);

// 是否可用
if (!OnPut(value))
{
Interlocked.Increment(ref _ReleaseCount);
return false;
}

if (value is DisposeBase db && db.Disposed)
{
Interlocked.Increment(ref _ReleaseCount);
return false;
}

var min = Min;

// 如果空闲数不足最小值,则返回到基础空闲集合
if (_FreeCount < min /*|| _free.Count < min*/)
_free.Push(pi);
else
_free2.Enqueue(pi);

// 最后时间
pi.LastTime = TimerX.Now;

Interlocked.Increment(ref _FreeCount);

// 启动定期清理的定时器
StartTimer();

return true;
}

/// <summary>归还时是否可用</summary>
/// <param name="value"></param>
/// <returns></returns>
protected virtual Boolean OnPut(T value) => true;
[Obsolete("Please use Return from 2024-02-01")]
public virtual Boolean Put(T value) => Return(value);

/// <summary>归还</summary>
/// <param name="value"></param>
Expand All @@ -247,7 +196,7 @@ public virtual Boolean Return(T value)
if (!_busy.TryRemove(value, out var pi))
{
#if DEBUG
WriteLog("Put Error");
WriteLog("Return Error");
#endif
Interlocked.Increment(ref _ReleaseCount);

Expand Down Expand Up @@ -483,7 +432,7 @@ protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);

Pool.Put(Value);
Pool.Return(Value);
}
#endregion
}
42 changes: 26 additions & 16 deletions NewLife.Core/Collections/Pool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,37 @@ struct Item

#region 构造
/// <summary>实例化对象池。默认大小CPU*2</summary>
/// <param name="max"></param>
/// <param name="max">最大对象数。默认大小CPU*2</param>
public Pool(Int32 max = 0)
{
if (max <= 0) max = Environment.ProcessorCount * 2;

Max = max;
}

/// <summary>实例化对象池。GC2时回收</summary>
/// <param name="max">最大对象数。默认大小CPU*2</param>
/// <param name="useGcClear">是否在二代GC时回收池里对象</param>
protected Pool(Int32 max, Boolean useGcClear) : this(max)
{
if (useGcClear) Gen2GcCallback.Register(s => (s as Pool<T>)!.OnGen2(), this);
}

private Int64 _next;
private Boolean OnGen2()
{
var now = Runtime.TickCount64;
if (_next <= 0)
_next = now + 60000;
else if (_next < now)
{
Clear();
_next = now + 60000;
}

return true;
}

[MemberNotNull(nameof(_items))]
private void Init()
{
Expand Down Expand Up @@ -75,21 +98,8 @@ public virtual T Get()
/// <summary>归还</summary>
/// <param name="value"></param>
/// <returns></returns>
public virtual Boolean Put(T value)
{
// 最热的一个对象在外层,便于快速存取
if (_current == null && Interlocked.CompareExchange(ref _current, value, null) == null) return true;

Init();

var items = _items;
for (var i = 0; i < items.Length; ++i)
{
if (Interlocked.CompareExchange(ref items[i].Value, value, null) == null) return true;
}

return false;
}
[Obsolete("Please use Return from 2024-02-01")]
public virtual Boolean Put(T value) => Return(value);

/// <summary>归还</summary>
/// <param name="value"></param>
Expand Down
77 changes: 77 additions & 0 deletions NewLife.Core/Common/Gen2GcCallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;

namespace NewLife;

/// <summary>Gen2垃圾回收回调</summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class Gen2GcCallback : CriticalFinalizerObject
{
private readonly Func<Boolean> _callback0;

private readonly Func<Object, Boolean> _callback1;

private GCHandle _weakTargetObj;

private Gen2GcCallback(Func<Boolean> callback) => _callback0 = callback;

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 17 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

private Gen2GcCallback(Func<Object, Boolean> callback, Object targetObj)

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.

Check warning on line 19 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Non-nullable field '_callback0' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
_callback1 = callback;
_weakTargetObj = GCHandle.Alloc(targetObj, GCHandleType.Weak);
}

/// <summary>
/// Registers a callback to be invoked during Gen2 garbage collection.
/// </summary>
/// <param name="callback">The callback function to be invoked.</param>
public static void Register(Func<Boolean> callback)
{
new Gen2GcCallback(callback);
}

/// <summary>
/// Registers a callback to be invoked during Gen2 garbage collection with a target object.
/// </summary>
/// <param name="callback">The callback function to be invoked.</param>
/// <param name="targetObj">The target object associated with the callback.</param>
public static void Register(Func<Object, Boolean> callback, Object targetObj)
{
new Gen2GcCallback(callback, targetObj);
}

~Gen2GcCallback()

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / build-publish

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'

Check warning on line 44 in NewLife.Core/Common/Gen2GcCallback.cs

View workflow job for this annotation

GitHub Actions / test

Missing XML comment for publicly visible type or member 'Gen2GcCallback.~Gen2GcCallback()'
{
if (_weakTargetObj.IsAllocated)
{
var target = _weakTargetObj.Target;
if (target == null)
{
_weakTargetObj.Free();
return;
}
try
{
if (!_callback1(target))
{
_weakTargetObj.Free();
return;
}
}
catch { }
}
else
{
try
{
if (!_callback0())
{
return;
}
}
catch { }
}
GC.ReRegisterForFinalize(this);
}
}
Loading

0 comments on commit a36923a

Please sign in to comment.