Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

memory leak unexplained #4674

Closed
lemmel opened this issue Feb 13, 2018 · 6 comments
Closed

memory leak unexplained #4674

lemmel opened this issue Feb 13, 2018 · 6 comments

Comments

@lemmel
Copy link

lemmel commented Feb 13, 2018

Hi !

I'm going further (I'm near to have a bridge Qt-ChakraCore; I will soon add property management through Proxy).

the problem

For now I was doing some tests about memory management (I didn't quite get all about memory) and I saw that there is a slow and steady memory increase when I'm repeatedly calling JsRun, asking it to run this instruction:

Toto.toto(5);

having in my function nothing but this:

  bool ok;
  //printf("====> argumentCount=%d\n", argumentCount);
  return JS_INVALID_REFERENCE;

so having nothing.

I altered my previous code #4669, as you can see below.
As the increase is done slighty over the time I made a video as a proof: memory_consumption.mp4.gz

I used valgrind for tracking the origin, and found that there "may be" some memory lost in several places (see the end of the log file: valgrind_output.txt)

And last but not least, I used in my test a call to the garbage collector, but it changed nothing.

my question

Is there something that I missed about memory management ?

hardware/software

code used for the test

#include "ChakraCore.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <cstring>
#define MAX 255
class ErrorCode // Just a helper for error management; you can ignore this
{
  JsErrorCode errCode;
public:
  ErrorCode():errCode(JsNoError){}
  ErrorCode(JsErrorCode errCode):errCode(errCode){}
#define EXCEPTION_MAX_LENGTH 255
  ErrorCode& operator=(JsErrorCode errorCode)
  {
    this->errCode = errorCode;
    if (errorCode == JsNoError)
      return *this;

    char buffer [EXCEPTION_MAX_LENGTH + 1];
    size_t actualLength;
    JsErrorCode error;

    JsValueRef exception;
    if ((error = JsGetAndClearException(&exception)) != JsNoError)
    {
      printf("Error on Error! Original error code=%d, new error code=%d\n", errorCode, error);
      return *this;
    }
    JsPropertyIdRef messageName;
    if ((error = JsCreatePropertyId("message", strlen("message"), &messageName)) != JsNoError)
    {
      printf("Error on Error! Original error code=%d, new error code=%d\n", errorCode, error);
      return *this;
    }
    JsValueRef messageValue;
    if ((error = JsGetProperty(exception, messageName, &messageValue)) != JsNoError)
    {
      printf("Error on Error! Original error code=%d, new error code=%d\n", errorCode, error);
      return *this;
    }
    if ((error = JsCopyString(messageValue, buffer, EXCEPTION_MAX_LENGTH, &actualLength)) != JsNoError)
    {
      printf("Error on Error! Original error code=%d, new error code=%d\n", errorCode, error);
      return *this;
    }
    buffer[actualLength] = 0;

    printf("Error: %s\n", buffer);
    exit(EXIT_FAILURE);
    return *this;
  }
};
JsErrorCode JsGetPropertyIdFromName(const char* string, size_t stringLen, JsPropertyIdRef *propertyId)
{
  return JsCreatePropertyId(
        string,
        stringLen,
        propertyId);
}
JsErrorCode registerFunction(JsValueRef hostObject, const char *callbackName, const size_t &length, JsNativeFunction callback, void *callbackState)
{
    JsErrorCode error;
    JsPropertyIdRef propertyId;
    JsValueRef function;
    if ((error = JsCreatePropertyId(callbackName, length,&propertyId)) != JsNoError) return error;
    if ((error = JsCreateFunction(callback, callbackState, &function)) != JsNoError) return error;
    if ((error = JsSetProperty(hostObject, propertyId, function, true)) != JsNoError) return error;
    return JsNoError;
}
JsValueRef theCaller(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
  bool ok;
  //printf("====> argumentCount=%d\n", argumentCount);
  return JS_INVALID_REFERENCE;
}
int main()
{
  JsRuntimeHandle runtime;
  JsValueRef globalObject;
  JsContextRef context;
  ErrorCode errCode     = JsNoError;
  errCode = JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime);
  errCode = JsCreateContext(runtime, &context);
  errCode = JsSetCurrentContext(context);
  errCode = JsGetGlobalObject(&globalObject);

  /*
   Now I want to create my "very own object": Toto !
   class Toto
   {
     public:
     void toto();
   };
  */
  JsValueRef object;//The instance of an object Toto
  JsPropertyIdRef objectPropertyId;
  errCode = JsCreateObject(&object);
  errCode = JsGetPropertyIdFromName("Toto", 4, &objectPropertyId);
  errCode = JsSetProperty(globalObject, objectPropertyId, object, true);
  errCode = registerFunction(object, "toto", 4, theCaller, nullptr);

  //Now to the JS code

  static unsigned currentSourceContext = 0;
  JsValueRef result;
  JsValueRef fname;
  errCode = JsCreateString("", 0, &fname);

  JsValueRef scriptSource;
  char jscode[] = "Toto.toto(5);";
  errCode = JsCreateString(jscode, strlen(jscode), &scriptSource);
  int garbageTrigger = 0;
  while(true)
  {
    errCode = JsRun(scriptSource, currentSourceContext++, fname, JsParseScriptAttributeNone, &result);
    if (garbageTrigger > 1000000)
    {
      printf("Garbage Collector\n");
      fflush(stdout);
      errCode = JsCollectGarbage(runtime);
      garbageTrigger = 0;
    }
    garbageTrigger++;
  }

  return 0;
}
@MSLaguana
Copy link
Contributor

I notice that you don't actually shut down the JS environment at all; are you just killing the process to end it and then watching valgrind's output? If you don't gracefully terminate the runtime then we don't get a chance to tear down the garbage collector etc.

@lemmel
Copy link
Author

lemmel commented Feb 16, 2018

You are perfectly right ! When the runtime is gracefully stopped, all the memory is restored and Valgrind complains no more about leaked memory.

I don't know if such a small "leak" (does look more of local variables that don't get cleaned) presents any risks. For the current time I'm just making little tests so I will not have the opportunity to dig the subject further.

@lemmel
Copy link
Author

lemmel commented Feb 16, 2018

I don't think that it is explained: why is the JS engine "eating" memory when doing nearly nothing ?

Yes there isn't a "leak" (perhaps because you use a memory pool, and by stopping the engine we free all the pool), but, on use, the engine seems to, at least, "lost" some memory.

But in the end, you are more competent than I am, so if you think that the topic is close, so be it[1].

[1] to avoid any misunderstanding, I mean it when I say that you know what you're doing; the english is not my native language, so please excuse me, if I'm offensing.

@rhuanjl
Copy link
Collaborator

rhuanjl commented Feb 16, 2018

I think I'm right in saying that any script provided to the RunScript is cached for the duration of the Context - this is intentional so that when a function in one script calls one in another it's still there for.

This is likely the cause of your increase in memory usage you're cache'ing many many copies of your script with that loop - it's all released when you close down the context.

This gradual increase would not happen if you ran one script containing the loop or if you called a function repeatedly with JsCallFunction rather than Parsing a new script over and over.

@lemmel
Copy link
Author

lemmel commented Feb 16, 2018

It seems logicial (even though it seems there is no new symbol "callable/reachable" added by my script: Toto.toto(5); is just a call), and would explain the slow increase: the script is a tiny one.
Thanks for the hint.

@lemmel
Copy link
Author

lemmel commented Feb 17, 2018

I found an example of reason to keep the code: the debug interface allows to reference precisely a script and line column (see JsDiagSetBreakpoint), so the code must be kept symbol "callable/reachable" or not.

Now I can have my mind at ease :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants