3

私が取り組んでいるこの IDS/IPS トラフィック モニタ コンソール アプリでのメモリ リークを突き止めました。エンティティ フレームワークだと思っていたのですが、ファイアウォール コードであることがわかりました。次のコードは、.net 4.6.2 では問題なく動作し、.net コアでもほとんど問題なく動作します。ただし、メモリ リークがあります (具体的には 2firewallRule行目)。

INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
INetFwRule firewallRule = firewallPolicy.Rules.OfType<INetFwRule>().Where(x => x.Name == fwRuleName).FirstOrDefault();

アプリは、できるだけ多くのメモリを使い果たすため、毎日アプリを再起動する必要があります。約 35MB で実行する必要があるときに、12GB の高さを見てきました。それも、その高値になるのに1日かそこらしかかかりません。したがって、基本的に への各呼び出しfirewallRuleは 5MB ~ 10MB を消費します。1 つのレコードまたは null レコードがそれだけの量のメモリを消費するのはおかしいと思われます。

firewallRule呼び出されているメソッド(およびそのアプリ)の他のすべての行を文字通りコメントアウトしたため、その行だと思います。最終的にその行をコメントアウトすると、リークが停止します。私の論理に欠陥がある場合はお知らせください。

テストしたい場合は、NetFwTypeLibそのコードを機能させるために参照を作成する必要があります。

なぜこれが起こっているのか、それを修正する方法を知っている人はいますか?

アップデート:

すべてをコメントアウトしていないと思われる方のために、元のコードを次に示します。

public static void FirewallSetup(string ip, string countryCode, bool isIpsString)
        {

        //    FirewallControl fwCtrl = new FirewallControl();
        //    fwCtrl.Block(ip, countryCode, isIpsString);
        //}

        //private void Block(string ip, string countryCode, bool isIpsString)
        //{
            var remoteAddresses = "*";
            string fwRuleName;
            bool createNewFwRule = false;

            //var fwRuleSuffix = SQLControl.GetFirewallRuleSuffix(countryCode).ToString();
            //if (string.IsNullOrEmpty(fwRuleSuffix) || fwRuleSuffix == "0")
            //{
                fwRuleName = fwRuleNamePrefix + countryCode;
            //}
            //else
            //{
            //    fwRuleName = fwRuleNamePrefix + countryCode + fwRuleSuffix;
            //}

            INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
            INetFwRule firewallRule = firewallPolicy.Rules.OfType<INetFwRule>().Where(x => x.Name == fwRuleName).FirstOrDefault();

            //if (firewallRule == null)
            //{
            //    createNewFwRule = true;
            //}
            //else
            //{
            //    //We need the following for creating the remoteAddresses string below
            //    //but, also need to count as Windows has a 5000 ip limit per rule
            //    remoteAddresses = firewallRule.RemoteAddresses;
            //    string[] aRemoteAddresses = remoteAddresses.Split(",");
            //    int remoteAddressesCount = aRemoteAddresses.Length;
            //    //Log.Debug(">>>Firewall remote addresses scope=" + remoteAddresses);
            //    Log.Warning(">>>" + fwRuleName + " Firewall remote addresses count:" + remoteAddressesCount);

            //    //5000 would be 0 to 4999 I think?
            //    if (remoteAddressesCount >= 4999)
            //    {
            //        //If remote ip scope is 5000, create a new fw rule
            //        var newFwRuleNameSuffix = SQLControl.GetNewFirewallRuleSuffix(countryCode).ToString();
            //        fwRuleName = fwRuleNamePrefix + countryCode + newFwRuleNameSuffix;

            //        createNewFwRule = true;
            //    }
            //}

            //If necessary, we create a new rule
            //TODO: Create another method for this?
            //if (createNewFwRule)
            //{
            //    firewallRule = (INetFwRule)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FWRule"));
            //    firewallRule.Name = fwRuleName;
            //    firewallPolicy.Rules.Add(firewallRule);
            //    firewallRule.Description = "Block inbound traffic from " + countryCode;
            //    firewallRule.Profiles = (int)NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_ALL;
            //    firewallRule.Protocol = (int)NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
            //    firewallRule.Direction = NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN;
            //    firewallRule.Action = NET_FW_ACTION_.NET_FW_ACTION_BLOCK;
            //    firewallRule.Enabled = true;
            //    //firewallRule.RemoteAddresses = ip;
            //    //firewallPolicy.Rules.Add(firewallRule); //throws error and not needed anyway
            //    //firewallRule.LocalPorts = "4000";
            //    //firewallRule.Grouping = "@firewallapi.dll,-23255";
            //    //firewallRule.Profiles = firewallPolicy.CurrentProfileTypes;
            //}

            //if (isIpsString || remoteAddresses == "*")
            //{
            //    firewallRule.RemoteAddresses = ip;
            //}
            //else
            //{
            //    firewallRule.RemoteAddresses = remoteAddresses + "," + ip;
            //}
        }

本番環境では、このメソッドを「そのまま」実行するだけで、メモリを消費します。繰り返しますが、.Net 4.6.1 (コメントなし) でこの同じコードを 1 年以上問題なく実行したため、これは .Net Core に固有のものです。

考えてコメントアウトしたので、ループに入れ、ここで内部的にトラブルシューティングすることもできます。レースだ…

現在実行されているサーバーは Windows Server 2016 です。

更新 2: Windows 10 でも再現可能です。エラーを再現するためのコード全体を次に示します。

using System;
using System.Linq;
using NetFwTypeLib;

namespace FirewallLeakTesterCore
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 0;
            while (count < 1000)
            {
                Console.WriteLine(count += 1);
                INetFwPolicy2 firewallPolicy = (INetFwPolicy2)Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwPolicy2"));
                INetFwRule firewallRule = firewallPolicy.Rules.OfType<INetFwRule>().Where(x => x.Name == "firewallRuleName").FirstOrDefault();

            }
            Console.ReadKey();
        }
    }
}

結果は次のとおりです。

Visual Studio 診断ツール:

Visual Studio 診断ツール

マイクロソフトのバグのようですか?これらのメモリ プロファイラーの 1 つに取り組んでみようと思いますが、問題が見つかった場合でも、どうすればよいでしょうか? これは、基本的な Windows ライブラリへの基本的な呼び出しであり、コメントの一部の人々が要点を見逃していると思います。

アプリのプロファイリングを記憶したり、問題を追跡したりすることを誰かに求めているわけではありません (決してそうではありませんでした... 私はすでにそれを行っています)。別の解決策を求めています。私が完全に使用できる構造またはlinqの問題またはその他のステートメントはありますか?

質問を閉じたままにしておきたい場合は、それで構いませんが、他の誰もこれに遭遇しないとは信じがたいです.

4

1 に答える 1