Archived Forum Post

Index of archived forum posts

Question:

memory leak occurring while using Chilkat.Socket

Apr 10 '13 at 17:10

OK, so I am not one to cry wolf, so believe me when I say that I have tried everything I know how to here...

I know this is a bit long, you might want to skim... this is just most of the steps I have gone through in this exceptionally long day :(

So I had this clever idea to monitor the connectivity between two processes communicating through Chilkat.Socket on different pc's. Not for the application logic, but as an indicator to the end user so that they can see if their are connectivity issues (maybe for troubleshooting?). Kind of like how your cell, or laptop indicates tower, or wireless router signal strength (respectively) in real time. Purely a convenience, but when things went wrong I began to question my entire implementation of the socket object.

It was very simple to implement, I just created a loop that fired every couple of seconds on the client and connected to the server, then used the success of that to formulate a simple display and closed the socket.(JScript BTW)

var strength = [];
connectivity = new bw.Loop(function() {
    var ssl = 0 ,
        ip = 'localhost' ,
        port = 5555 ,
        maxWaitMillisec = 5 ,
        success = socket.Connect(ip , port , ssl , maxWaitMillisec);

    if (success) {

        if (strength.length <= 5)
            strength.push('I');

        socket.Close(10);
    }
    else {
        if (strength.length > 0)
            strength.pop();
    }
    bw.ID('networkDisp').innerText = strength.join('');
})
connectivity.speed(2000);
connectivity.init();

But as I left that alone to go work on other things, when I came back my memory had jumped to 200+mb (on the server app)

So I stripped everything out of both scripts that wasn't necessary (as illustrated by the chilkat example programs) and ran the following

(server)

var listenSocket = new ActiveXObject('Chilkat.Socket');
listenSocket.UnlockComponent('****');
listenSocket.BindAndListen(5555 , 1);

var repeat = function() {

    var connectedSocket = listenSocket.AcceptNextConnection(5);

    if (connectedSocket) {
        connectedSocket.Close(1)
    }

    repeat();
}
repeat();

(client)

var socket = new ActiveXObject('Chilkat.Socket');
socket.UnlockComponent('****');

var repeat = function() {

    var success = socket.Connect('localhost' , 5555 , 0 , 5);

    if (success)
        socket.Close(10);

    repeat();
};
repeat();

I tried other various settings (variations in the maxWaitMillisec, as well as MaxReadIdleMs & MaxSendIdleMs properties) but the server always grew in memory (quite fast) and the client stayed the same. After a while I turned the server off, but I forgot the client, and then all of a sudden line 6 var success = socket.Connect('localhost' , 5555 , 0 , 5); in the client interrupted my research with a crash: "Stack Overflow". No idea where that came from... but I changed the maxWaitMillisec to 0 (after all, the application can hang during testing...) and that stopped that.

So I tried translating to vbscript (I know it's the same activeX, but it seemed worth a shot). Still had the memory leak (as expected). I thought maybe I wasn't giving the socket close enough time, so I extended that to 1000ms, and slept for 1500ms.

set listenSocket = CreateObject("Chilkat.Socket")
listenSocket.UnlockComponent("****")
success = listenSocket.BindAndListen(5555,25)

Function repeat()

    Set connectedSocket = listenSocket.AcceptNextConnection(0)

    If (connectedSocket Is Nothing ) Then
        repeat()
    End If

    connectedSocket.Close 1000
    WScript.Sleep(1500)
    repeat()

End Function

repeat()

Still leaked.

I downloaded the latest activeX (dec 2012), still leaked.

I compiled the JScript with JSC.exe (which if you are unfamiliar requires .net as per my previous issue that you addressed in "Chilkat.FileAccess not marked as "safe for scripting"?"). So I downloaded the latest ChilkatDotNet.dll (dec 2012) and compiled/ran the following

import Chilkat

var listenSocket = new Chilkat.Socket
listenSocket.UnlockComponent('****');
listenSocket.BindAndListen(5555 , 1);

var repeat = function() {

    var connectedSocket = listenSocket.AcceptNextConnection(0);

    if (connectedSocket) {
        connectedSocket.Close(10)
    }

    repeat();
}
repeat();

Leaked... Still.

I have been scouring the reference material to find which property or method I incorrectly allocated, or omitted but all I can figure is the Dispose() method (that I have been waiting about 6 months to be "documented"), and even though I have no idea how to use it I took a shot in the dark. connectedSocket.Dispose() didn't seem to register... but,

The following caused almost an immediate crash

var listenSocket = new ActiveXObject('Chilkat.Socket');
listenSocket.UnlockComponent('****');
listenSocket.BindAndListen(5555 , 1);

var repeat=function() {

    var connectedSocket = listenSocket.AcceptNextConnection(0);

    if (connectedSocket) {
        connectedSocket.Close(1)
    }
    listenSocket.Dispose()
    repeat();
}
repeat();

So I thought maybe if I "dispose" of the object, I should allow the garbage collection to sweep it up, and just call a new instance. And the following actually slowed the "leak" (a bit)

var repeat=function() {

    var listenSocket = new ActiveXObject('Chilkat.Socket');
    listenSocket.UnlockComponent('****');
    listenSocket.BindAndListen(5555 , 1);

    var connectedSocket = listenSocket.AcceptNextConnection(0);

    if (connectedSocket) {
        connectedSocket.Close(1)
    }
    listenSocket.Dispose()
    repeat();
}
repeat();

But, of course, that did not stop it. Since it is undocumented on the chilkat sites I did a google search for "Dispose method" and in the msdn article their was mention of "Important: C++ programmers should read" and I remembered, that chilkat uses the same c++ code for all libs across all platforms, so their must be documentation in http://www.chilkatsoft.com/refdoc/vcCkSocketRef.html about Dispose()... but it doesn't even exist there... so I'm definitely confused... I only mention that so that you understand that: I tried... and I am at my whits end.

What have I omitted, or improperly calibrated in my code?

Keep in mind that while the client doesn't grow, if left unchecked it will eventually cause a stack overflow with var success = socket.Connect('localhost' , 5555 , 0 , 5);

And the server seems to inevitably fail at var connectedSocket = listenSocket.AcceptNextConnection(0);

I know I'm supposed to provide LastErrorText, but it seems to be working until it doesn't, and (the main issue) it works fine but leaks memory... so I honestly don't know where to poll the LastErrorText...

Very sorry for the long post (if I knew what I was talking about maybe it would have been more concise). And thank you for taking the time to consider it.


Accepted Answer

You are correct: The problem was in the ActiveX wrapper. Please try this new build: http://www.chilkatsoft.com/preRelease/ChilkatSocket.zip


Answer

It appears that you are calling the repeat() function from within itself. This will eventually fill up the stack because repeat() never reaches the end of the function before calling itself (hence the Stack Overflow error).

You should use a timer or some other event to call repeat(), and then let the repeat() method finish without calling itself recursively.


Answer

UPDATE

Ok, so I tried http://www.chilkatsoft.com/p/p_300.asp, but this took quite a bit of research as my jsc compiler runs on .net ver 2. I found a ver 4.0 compiler, and downloaded ChilkatDotNet4.dll and ran the following for the server

import Chilkat
import System

var listenSocket = new Chilkat.Socket
var fac = new Chilkat.FileAccess

listenSocket.UnlockComponent('****');
listenSocket.BindAndListen(5555 , 1);

fac.WriteEntireTextFile('serverLog.txt' , 'begin' , 'ansi')

for (var iter = 0 ; iter<5000 ; ++iter) {

    var connectedSocket = listenSocket.AcceptNextConnection(0);

    if (connectedSocket) {
        connectedSocket.Close(10)
    }

    else {
        fac.OpenForAppend('serverLog.txt')
        fac.AppendAnsi(fac.LastErrorText)
        fac.FileClose()
    }

    GC.Collect()
    GC.WaitForPendingFinalizers();

}

This performed exactly as it should. There must not be a memory leak in AcceptNextConnection. (as I'm sure you anticipated)

Additionally I ran my original test to the point where it acquired 200+ memory again, and killed the client. It took a few hours, but the excess memory finally subsided. So my hypothesis is that the issue might be in the ActiveX wrapper. The windows scripting host gives me no direct access to GC, so all I have been able to try is to nullify the orignial object and create a new one, but as I stated before, that doesn't work... It takes some time, but it eventually clears its own memory, is there any way that you can provide a method to do that programmatically? (assuming I'm correct...)

If not, I will have no choice but to develop this portion of my application with .net (which admittedly would be a fun and welcome challenge...)


Answer

Still don't have an answer (Sorry), but I'm curious about the "leak" too as I've just started work on a web server using the Chilkat Socket for the listener.

When I connect to my CK server using a web browser and hold down F5 (refresh) for a while to open lots of connections, I see the memory use climb steadily. After releasing F5, closing & disposing of all of my accepted sockets, the memory use remains high, even after waiting some time (just in case the memory is being cached for a while).

Is this a leak, or are we missing a step to free memory?