0

I've found increasing memory usage in my windows service that has apple push notification service. So I just separated the APN part into console application and tested it.

The console app is supposed to use 1 min timer and make a new connection APN server on elapsed timer event.

My code is like below

class Program
{         
    static ApplePushNotification.PushNotificationService apn = new ApplePushNotification.PushNotificationService(ApplePushNotification.ServiceEnvironments.Production);
    const double interval60Mins = 1 * 60 * 1000;
    static System.Timers.Timer checkForTime = new System.Timers.Timer(interval60Mins);

    static void Main(string[] args)
    {
        checkForTime.Elapsed += new System.Timers.ElapsedEventHandler(checkForTime_Elapsed);
        checkForTime.Enabled = true;

        apn.Connect();
        Console.ReadKey();
    }
    static void checkForTime_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine("Timer elapsed at " + DateTime.UtcNow.ToString());
        Console.WriteLine("Memory used : " + GC.GetTotalMemory(true));
        apn.Close();
        apn.Connect();
    }
}


public class PushNotificationService
{
    public const string PUSH_SANDBOX = "gateway.sandbox.push.apple.com";
    public const string PUSH_PRODUCTION = "gateway.push.apple.com";
    public X509Certificate2 certificate = null;
    public const string PRODUCTION_CERTIFICATE_SUBJECT_NAME = "blind";
    public const int PUSH_PORT = 2195;
    private static SslStream sslStream = null;
    private static System.Net.Sockets.TcpClient client = null;
    public CancellationTokenSource tokenSource = null;
    Task read, write;

    public PushNotificationService(ServiceEnvironments serviceEnvironment)
    {
        this.serviceEnvironment = serviceEnvironment;
    }
    private void GetCertificate()
    {
        string certificate_name = string.Empty;
        X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        X509Certificate2Collection certificates = null;
        store.Open(OpenFlags.ReadOnly);
        X509Certificate2 result = null;
        certificates = store.Certificates;
        store.Close();

        if (serviceEnvironment == ServiceEnvironments.Production)
        {
            this.hostName = PUSH_PRODUCTION;
            certificate_name = PRODUCTION_CERTIFICATE_SUBJECT_NAME;
        }
        else
        {
            this.hostName = PUSH_SANDBOX;
            certificate_name = SANDBOX_CERTIFICATE_SUBJECT_NAME;
        }
        foreach (X509Certificate2 cert in certificates)
        {
            if (cert.SubjectName.Name == certificate_name)
            {
                certificate = new X509Certificate2(cert);
                //System.Diagnostics.Debug.WriteLine("Certificate Found : " + hostname);
                break;
            }
        }

        if (certificate == null)
        {
            Console.WriteLine("Unable to find correct certificate");
            throw new Exception("Certficate was not found");
        }

    }
    public void Connect()
    {
       try
        {
            tokenSource = new CancellationTokenSource();
            var token = tokenSource.Token;
            if (this.certificate == null) GetCertificate();

            client = new System.Net.Sockets.TcpClient();

            //connecting to the push interface
            client.Connect(hostName, PUSH_PORT);
            sslStream = new SslStream(client.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
            var certificateCollections = new X509Certificate2Collection(this.certificate);
            try
            {
                sslStream.AuthenticateAsClient(hostName, certificateCollections, SslProtocols.Default, true);
            }
            catch (AuthenticationException e)
            {
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: " + e.InnerException.Message);
                }
                Console.WriteLine("Authentication failed - closing the connection.");
                client.Close();
            }
            catch (Exception ex)
            {

            }
            read = Task.Factory.StartNew(() => Read(sslStream, token), token);
            write = Task.Factory.StartNew(() => Write(sslStream, token), token);
            Console.WriteLine("APN Started " + DateTime.UtcNow.ToString());
            Console.WriteLine();
        }
        catch (Exception e)
        {

        }
    }
    public void Close()
    {
        tokenSource.Cancel();
        if (read != null) read.Wait();
        if (write != null) write.Wait();
        read.Dispose();
        write.Dispose();
        client.Close();
    }
}

As a result of this console, I can see memory usage keep increasing.

The console output is like below.

Timer elapsed at 11/23/2016 10:26:44 AM Memory used : 2621688 APN Started 11/23/2016 10:26:44 AM

Timer elapsed at 11/23/2016 10:27:44 AM Memory used : 2607156 APN Started 11/23/2016 10:27:44 AM

...

Timer elapsed at 11/23/2016 11:16:44 AM Memory used : 4055948 APN Started 11/23/2016 11:16:44 AM

...

Timer elapsed at 11/23/2016 1:38:44 PM Memory used : 6706152 APN Started 11/23/2016 1:38:44 PM

...

Timer elapsed at 11/23/2016 2:12:44 PM Memory used : 8937776 APN Started 11/23/2016 2:12:45 PM

Can anyone help me know what's happening?

Thanks,

jay noh
  • 47
  • 2
  • 1
    8937776 is only ~9 megabytes. I'm no GC expert, but it will not free memory unless there is "pressure" to do so, and IMO 9mb is very close to "no pressure at all to free up memory yet". A better test is to call `GC.Collect();`, `GC.WaitForPendingFinalizers();`, then `GC.Collect();` a [second time](http://stackoverflow.com/questions/4257372/how-to-force-garbage-collector-to-run#comment4612993_4257387), THEN call `GC.GetTotalMemory(true)` to see if memory usage is growing. Only do this for testing, once you verify there is no memory leak remove the new GC calls and let it handle itself. – Quantic Nov 23 '16 at 19:53
  • I agree with @Quantic. Alos you can get a memory profiler take a memory snapshot and look what objects are held in memory and why. – Ed Pavlov Nov 27 '16 at 15:44
  • Thanks for your help guys :) – jay noh Nov 28 '16 at 23:04

0 Answers0