限制C#方法的最大内存使用量

时间:2017-09-28 07:50:53

标签: c# .net

有没有办法限制允许C#方法分配的最大内存?我们正在处理复杂的用户提供的输入,我们意识到可以通过提供一定的特殊输入来帮助我们的服务。我们无法检测到DoS攻击的所有变化,因此我们希望限制ProcessInput()方法的处理时间和内存分配。

处理时间“简单”,我们只需运行一个计时器并通过CancellationToken取消ProcessInput操作。但是,我们还没有找到(简单)内存分配的解决方案。我们不想编写自己的内存管理器并使用对象数组或类似的东西。

1 个答案:

答案 0 :(得分:1)

GC堆是每个进程,而不是每个AppDomain - 所以为了正确估计,您需要生成一个单独的进程。

您甚至可以自己托管CLR以影响细分受众群规模并获取通知。但是,如果在此过程中没有其他任何内容正在运行,那么您可以通过估算然后使用GC.GetTotalMemory()。然而,这需要在NoGC'如果您对方法运行期间消耗的总内存感兴趣,那么而不是在任何时间点使用的最大总内存量。 - 因为GC可以在方法运行期间多次触发。

要限制产生进程的性能/资源影响,您可以生成N个进程 - 其中N是您所需的并发级别 - 并且让每个进程从中央进程中的工作窃取队列中提取任务,而子进程同步处理请求。

一个肮脏的想法是什么样的(你需要处理结果报告以及其他100个'次要的事情):

主要流程:

    public void EnqueueWork(WorkRequest request)
    {
        _workQueue.Enqueue(request);
    }

    ConcurrentQueue<WorkRequest> _workQueue = new ConcurrentQueue<WorkRequest>();

    [OperationContract]
    public bool GetWork(out WorkRequest work)
    {
        return _workQueue.TryDequeue(out work);
    }

工作人员流程:

    public void ProcessRequests()
    {
        WorkRequest work;
        if (GetWork(out work))
        {
            try
            {
                //this actually preallocates 2 * _MAX_MEMORY - for ephemeral segment and LOH
                // it also performs full GC collect if needed - so you don't need to call it yourself
                if (!GC.TryStartNoGCRegion(_MAX_MEMORY))
                {
                    //fail
                }

                CancellationTokenSource cts = new CancellationTokenSource(_MAX_PROCESSING_SPAN);
                long initialMemory = GC.GetTotalMemory(false);

                Task memoryWatchDog = Task.Factory.StartNew(() =>
                {
                    while (!cts.Token.WaitHandle.WaitOne(_MEMORY_CHECK_INTERVAL))
                    {
                        if (GC.GetTotalMemory(false) - initialMemory > _MAX_MEMORY)
                        {
                            cts.Cancel();
                            //and error out?
                        }
                    }
                })

                DoProcessWork(work, cts);

                cts.Cancel();

                GC.EndNoGCRegion();
            }
            catch (Exception e)
            {
                //request failed
            }
        }
        else
        {
            //Wait on signal from main process
        }
    }