GC and userdata objects

classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

GC and userdata objects

Dibyendu Majumdar
Hi,

One practical issue currently with Lua's GC is it slack of knowledge
about memory held by userdata objects. This makes the GC calculation
pretty erroneous - and userdata objects are pretty common I presume.
Is there a way to make the GC aware of the memory held by a userdata
object - maybe through an C API call?

In my view this would be a very useful enhancement to Lua.

Thanks and Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Francisco Olarte
On Wed, Apr 11, 2018 at 11:17 AM, Dibyendu Majumdar
<[hidden email]> wrote:
> One practical issue currently with Lua's GC is it slack of knowledge
> about memory held by userdata objects. This makes the GC calculation
> pretty erroneous - and userdata objects are pretty common I presume.
> Is there a way to make the GC aware of the memory held by a userdata
> object - maybe through an C API call?

The GC already knows about part of the memory held by the object.

The other is difficult to define.

I mean. We use C++. We put the main object into the lua heap, lua
knows about it. But our objects hold other memory consuming resources.
Some are owned, some are shared, some are ref counted, some are not.
And some are not even ours, like windows handles, file buffers and the
like, and some hold more resources. It would be very difficult for us
to tell you how much memory an object uses from C++.

> In my view this would be a very useful enhancement to Lua.

It could be, but you would need to make it much more smart to take
into account memory external to lua heap. AFAIK the GC works only
across a sinle lua state.

Francisco Olarte.

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Reinder Feenstra
On Wed, 11 Apr 2018, 12:17 Francisco Olarte, <[hidden email]> wrote:
On Wed, Apr 11, 2018 at 11:17 AM, Dibyendu Majumdar
<[hidden email]> wrote:
> One practical issue currently with Lua's GC is it slack of knowledge
> about memory held by userdata objects. This makes the GC calculation
> pretty erroneous - and userdata objects are pretty common I presume.
> Is there a way to make the GC aware of the memory held by a userdata
> object - maybe through an C API call?

The GC already knows about part of the memory held by the object.

The other is difficult to define.

I mean. We use C++. We put the main object into the lua heap, lua
knows about it. But our objects hold other memory consuming resources.
Some are owned, some are shared, some are ref counted, some are not.
And some are not even ours, like windows handles, file buffers and the
like, and some hold more resources. It would be very difficult for us
to tell you how much memory an object uses from C++.


A possible solution could be if you we're able to hint the GC a virtual size for the userdata.

I'd had the same issue with calls to lua from C++ with userdata. The userdata only contained a std::shared_ptr, so it was small for lua, but it contained "virtually" a data chunk that could be a few MB up to 256 MB.
Under some conditions up to 1000 of them where kept alive until the GC decided to clean up some of them. We "solved" it by making the GC very agressive, to make sure unused reference were cleaned up fast.
I've not yet found a better solution.
 
> In my view this would be a very useful enhancement to Lua.

It could be, but you would need to make it much more smart to take
into account memory external to lua heap. AFAIK the GC works only
across a sinle lua state.

Francisco Olarte.


Reinder
Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
In reply to this post by Francisco Olarte
On 11 April 2018 at 11:16, Francisco Olarte <[hidden email]> wrote:

> On Wed, Apr 11, 2018 at 11:17 AM, Dibyendu Majumdar
> <[hidden email]> wrote:
>> One practical issue currently with Lua's GC is it slack of knowledge
>> about memory held by userdata objects. This makes the GC calculation
>> pretty erroneous - and userdata objects are pretty common I presume.
>> Is there a way to make the GC aware of the memory held by a userdata
>> object - maybe through an C API call?
>
> The GC already knows about part of the memory held by the object.
>
> The other is difficult to define.
>
> I mean. We use C++. We put the main object into the lua heap, lua
> knows about it. But our objects hold other memory consuming resources.
> Some are owned, some are shared, some are ref counted, some are not.
> And some are not even ours, like windows handles, file buffers and the
> like, and some hold more resources. It would be very difficult for us
> to tell you how much memory an object uses from C++.
>

Sure, but that is the library developer's responsibility. If an API
existed to allow a library to set memory size associated with userdata
that would be a building block for libraries to inform the GC.
Sometimes this info may be approximate or the library may not be able
to provide it, but even so this is better than the current situation
where the GC is oblivious of the actual size of userdata objects.

Note that I am talking of fulluserdata objects here which are managed by GC.

The workaround I have used is to force full GC every so often which
defeats the idea of incremental GC.

Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Roberto Ierusalimschy
Maybe there is a misunderstanding of how the GC works. What moves the GC
is not the amount of memory being used, but the amount of memory being
allocated. If you are using 100GB, frees them all, and do not allocate
anything else, the GC will not collect anything.

So, keeping some huge userdata is not particularly relevant to the
GC. What is relevant is the allocation of a huge userdata. You can
communicate this to the GC by calling collectgarbage("step", x) with 'x'
proportional to the size of the userdata.

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
On 11 April 2018 at 22:45, Roberto Ierusalimschy <[hidden email]> wrote:
> So, keeping some huge userdata is not particularly relevant to the
> GC. What is relevant is the allocation of a huge userdata. You can
> communicate this to the GC by calling collectgarbage("step", x) with 'x'
> proportional to the size of the userdata.
>

Hi. I have tried using GC step before but did not quite succeed in
resolving similar issues but that could have been due to my lack of
understanding. Is there an example of how it should be used?

The scenario is this - I am creating a AST generator for Lua; the AST
is captured in a userdata. The AST is generated every time some code
is parsed - so you can imagine that this can be quite frequent. Say in
a test program, many code chunks are parsed resulting in several
userdata objects of different sizes being created. I noticed that the
GC does not collect the objects fast enough. In this scenario, how do
you suggest I should use the GC step parameter? Keep setting it to
different values based on userdata sizes? Or keep track of total
userdata memory and set this accordingly? Obviously the rate of
allocations varies over time, and sizes vary as well.

Thanks and Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
On 12 April 2018 at 06:31, Dibyendu Majumdar <[hidden email]> wrote:

> On 11 April 2018 at 22:45, Roberto Ierusalimschy <[hidden email]> wrote:
>> So, keeping some huge userdata is not particularly relevant to the
>> GC. What is relevant is the allocation of a huge userdata. You can
>> communicate this to the GC by calling collectgarbage("step", x) with 'x'
>> proportional to the size of the userdata.
>>
>
> Hi. I have tried using GC step before but did not quite succeed in
> resolving similar issues but that could have been due to my lack of
> understanding. Is there an example of how it should be used?
>
> The scenario is this - I am creating a AST generator for Lua; the AST
> is captured in a userdata. The AST is generated every time some code
> is parsed - so you can imagine that this can be quite frequent. Say in
> a test program, many code chunks are parsed resulting in several
> userdata objects of different sizes being created. I noticed that the
> GC does not collect the objects fast enough. In this scenario, how do
> you suggest I should use the GC step parameter? Keep setting it to
> different values based on userdata sizes? Or keep track of total
> userdata memory and set this accordingly? Obviously the rate of
> allocations varies over time, and sizes vary as well.
>

Just to clarify - a single fulluserdata object is created per AST -
but internally this holds its own set of data structures which are
allocated using an internal Arena based allocator. So the userdata
object is small but can point to large chunk of memory.

Also the memory gets freed when the __gc metamethod is called.

Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

云风 Cloud Wu
In reply to this post by Dibyendu Majumdar
Dibyendu Majumdar <[hidden email]>于2018年4月12日周四 下午1:32写道:
 
The scenario is this - I am creating a AST generator for Lua; the AST
is captured in a userdata. The AST is generated every time some code
is parsed - so you can imagine that this can be quite frequent. Say in
a test program, many code chunks are parsed resulting in several
userdata objects of different sizes being created. I noticed that the
GC does not collect the objects fast enough. In this scenario, how do
you suggest I should use the GC step parameter? Keep setting it to
different values based on userdata sizes? Or keep track of total
userdata memory and set this accordingly? Obviously the rate of
allocations varies over time, and sizes vary as well.

I don't think make GC more aggressive by the actual memory size annotation would be helpful in this scenario.

My solution is putting all  AST in one C container . Userdata only stores an ID that associate to the AST, and reference the code (string or function) in a weak table.

And then we can remove some older  AST  (by LRU) every time new code is parsed, If we need an AST already removed, just rebuild from code again.

The C container of AST is only a cache, so we can also clear the cache when we need more memory.




 


 
Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
On 12 April 2018 at 07:58, 云风 Cloud Wu <[hidden email]> wrote:

> Dibyendu Majumdar <[hidden email]>于2018年4月12日周四 下午1:32写道:
>
>>
>> The scenario is this - I am creating a AST generator for Lua; the AST
>> is captured in a userdata. The AST is generated every time some code
>> is parsed - so you can imagine that this can be quite frequent. Say in
>> a test program, many code chunks are parsed resulting in several
>> userdata objects of different sizes being created. I noticed that the
>> GC does not collect the objects fast enough. In this scenario, how do
>> you suggest I should use the GC step parameter? Keep setting it to
>> different values based on userdata sizes? Or keep track of total
>> userdata memory and set this accordingly? Obviously the rate of
>> allocations varies over time, and sizes vary as well.
>
>
> I don't think make GC more aggressive by the actual memory size annotation
> would be helpful in this scenario.
>
> My solution is putting all  AST in one C container . Userdata only stores an
> ID that associate to the AST, and reference the code (string or function) in
> a weak table.
>

At the moment I am putting all nodes for a single chunk into one C
container; so:

x = ast.parse 'return'

Creates a fulluserdata object that has all the AST for that chunk.

But if I do:

y = ast.parse 'if x then return 42 end'

This creates a separate fulluserdata object separate from the one
before. Again all the real structure is inside the C data structure.

Are you saying that the both of these should live in the same C data structure?


> And then we can remove some older  AST  (by LRU) every time new code is
> parsed, If we need an AST already removed, just rebuild from code again.
>
> The C container of AST is only a cache, so we can also clear the cache when
> we need more memory.
>

I suppose I could also have a method ast:release() that gets rid of
the underlying C data structure; this can be used when the code no
longer needs the AST.


Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

云风 Cloud Wu
Dibyendu Majumdar <[hidden email]>于2018年4月12日周四 下午3:18写道:

At the moment I am putting all nodes for a single chunk into one C
container; so:

x = ast.parse 'return'

Creates a fulluserdata object that has all the AST for that chunk.

But if I do:

y = ast.parse 'if x then return 42 end'

This creates a separate fulluserdata object separate from the one
before. Again all the real structure is inside the C data structure.

Are you saying that the both of these should live in the same C data structure?

I mean AST data structures can be rebuild from the code string, so the container is only a cache. Yes, both of these can live in one cache. And we can clear the cache at any time.

x = ast.parse 'return'   make an userdata just [id:1] , and put [userdata:'return'] into a weak table. This userdata is a small one .

The real AST is in the C AST cache structure like  [ 1:AST for 'return' , ... ] .

When we call some ast api later , remove some less used AST in this cache just like collect garbage. It's in dependent of lua gc , so it can run more aggressive.

> And then we can remove some older  AST  (by LRU) every time new code is
> parsed, If we need an AST already removed, just rebuild from code again.
>
> The C container of AST is only a cache, so we can also clear the cache when
> we need more memory.
>

I suppose I could also have a method ast:release() that gets rid of
the underlying C data structure; this can be used when the code no
longer needs the AST.

Manually management by ast:release() can work, but use LRU algorithm to release automatically would be easier and less bug (IMHO) . If we hold some AST for later use, it's safe to release automatically first, and rebuild from code when use again.
  
Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Francisco Olarte
In reply to this post by Reinder Feenstra
On Wed, Apr 11, 2018 at 9:00 PM, Reinder Feenstra
<[hidden email]> wrote:
> A possible solution could be if you we're able to hint the GC a virtual size
> for the userdata.

Yeah, that may be nice. A single word can contain it, but you would
need to hint it right, if you have a forest you need to hint the
forest size, ....

> I'd had the same issue with calls to lua from C++ with userdata. The
> userdata only contained a std::shared_ptr, so it was small for lua, but it
> contained "virtually" a data chunk that could be a few MB up to 256 MB.
> Under some conditions up to 1000 of them where kept alive until the GC
> decided to clean up some of them. We "solved" it by making the GC very
> agressive, to make sure unused reference were cleaned up fast.
> I've not yet found a better solution.

And there comes another problem. I have many objects with shared ptr
to them stored in userdata, and then they contain a lot of shared ptr
internally.

Estimating the full size of reachable memory from each userdata would
lead the GC to think they consume many times more than they usually
do.

Also, there are shared ptrs unknown to lua, so even without external
sharing lua can overstimate the amount of memory it's going to free (
or the used one ), but this is probably harmless.

What I've found is in directly-controled memory, lua owned objects,  I
can just allocate the chunk in the user data ( and slice it with
placement new if needed ), but for complex stuff I just do not have
enough control of how much would be freed, and it does not normally
matter anyway.

Francisco Olarte.

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Viacheslav Usov
Top posting, since I am replying to the entire thread.

This discussion brings back to my memory some older discussions in the list, about deterministic collection/finalization. The principal issue is that some userdata are much more than a handful of bytes, they can be handles, connections, money and what not. I think there is a need to be able to mark a userdarum so that this particular userdatum (and everything that references it) becomes subject to very aggressive collection, perhaps with ref-counting. The argument "this is bad for performance" has been duly noted. I think it is possible to have two major branches in the VM interpreter, the regular branch that works like it does today (without any extra bookkeeping for any object), and the "aggressive" branch which is switched into when there is at least one userdatum marked as described above, and exited as soon as they are all gone. That way the price for the extra bookkeeping will only be paid by those who are willing to pay it.

Cheers,
V.

On Thu, Apr 12, 2018 at 10:28 AM, Francisco Olarte <[hidden email]> wrote:
On Wed, Apr 11, 2018 at 9:00 PM, Reinder Feenstra
<[hidden email]> wrote:
> A possible solution could be if you we're able to hint the GC a virtual size
> for the userdata.

Yeah, that may be nice. A single word can contain it, but you would
need to hint it right, if you have a forest you need to hint the
forest size, ....

> I'd had the same issue with calls to lua from C++ with userdata. The
> userdata only contained a std::shared_ptr, so it was small for lua, but it
> contained "virtually" a data chunk that could be a few MB up to 256 MB.
> Under some conditions up to 1000 of them where kept alive until the GC
> decided to clean up some of them. We "solved" it by making the GC very
> agressive, to make sure unused reference were cleaned up fast.
> I've not yet found a better solution.

And there comes another problem. I have many objects with shared ptr
to them stored in userdata, and then they contain a lot of shared ptr
internally.

Estimating the full size of reachable memory from each userdata would
lead the GC to think they consume many times more than they usually
do.

Also, there are shared ptrs unknown to lua, so even without external
sharing lua can overstimate the amount of memory it's going to free (
or the used one ), but this is probably harmless.

What I've found is in directly-controled memory, lua owned objects,  I
can just allocate the chunk in the user data ( and slice it with
placement new if needed ), but for complex stuff I just do not have
enough control of how much would be freed, and it does not normally
matter anyway.

Francisco Olarte.


Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

steve donovan
On Thu, Apr 12, 2018 at 12:41 PM, Viacheslav Usov <[hidden email]> wrote:
> about deterministic collection/finalization. The principal issue is that
> some userdata are much more than a handful of bytes, they can be handles,
> connections, money and what not.

This is the important point. I first encountered this issue when doing
Lua on top of Java in Android - the userdata could contain really big
objects like images, but the Lua GC had no way of knowing this. A GC
on top of a GC! I recall that Eric Wing had similar issues with his
ObjectiveC/Lua bridge.

Although, at least on Android there is a lot of explicit resource
dropping, because there's not that much memory to wait for the GC to
do its (slow) magic.

I must say I like the idea of deterministic resource finalization,
although I understand it has runtime implications.

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
In reply to this post by 云风 Cloud Wu
On 12 April 2018 at 08:49, 云风 Cloud Wu <[hidden email]> wrote:

> Dibyendu Majumdar <[hidden email]>于2018年4月12日周四 下午3:18写道:
>>
>>
>> At the moment I am putting all nodes for a single chunk into one C
>> container; so:
>>
>> x = ast.parse 'return'
>>
>> Creates a fulluserdata object that has all the AST for that chunk.
>>
>> But if I do:
>>
>> y = ast.parse 'if x then return 42 end'
>>
>> This creates a separate fulluserdata object separate from the one
>> before. Again all the real structure is inside the C data structure.
>>
>> Are you saying that the both of these should live in the same C data
>> structure?
>
>
> I mean AST data structures can be rebuild from the code string, so the
> container is only a cache. Yes, both of these can live in one cache. And we
> can clear the cache at any time.
>
> x = ast.parse 'return'   make an userdata just [id:1] , and put
> [userdata:'return'] into a weak table. This userdata is a small one .
>
> The real AST is in the C AST cache structure like  [ 1:AST for 'return' ,
> ... ] .
>
> When we call some ast api later , remove some less used AST in this cache
> just like collect garbage. It's in dependent of lua gc , so it can run more
> aggressive.
>

Okay understood.

>> > And then we can remove some older  AST  (by LRU) every time new code is
>> > parsed, If we need an AST already removed, just rebuild from code again.
>> >
>> > The C container of AST is only a cache, so we can also clear the cache
>> > when
>> > we need more memory.
>> >
>>
>> I suppose I could also have a method ast:release() that gets rid of
>> the underlying C data structure; this can be used when the code no
>> longer needs the AST.
>
>
> Manually management by ast:release() can work, but use LRU algorithm to
> release automatically would be easier and less bug (IMHO) . If we hold some
> AST for later use, it's safe to release automatically first, and rebuild
> from code when use again.
>

In my case the ultimate goal is code generation - so the AST will most
likely be used immediately after and then never used again. A manual
release might be easiest option here.

But of course the AST can be used for other purposes too - for
instance by someone else that needs it ... in that case a different
strategy is necessary. But you are right I could cache the source
string / and AST as a pair - and just rebuild AST from source if
necessary. But I will need to hang on to the source until ... ?

Thanks and Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
In reply to this post by steve donovan
On 12 April 2018 at 12:53, steve donovan <[hidden email]> wrote:

> On Thu, Apr 12, 2018 at 12:41 PM, Viacheslav Usov <[hidden email]> wrote:
>> about deterministic collection/finalization. The principal issue is that
>> some userdata are much more than a handful of bytes, they can be handles,
>> connections, money and what not.
>
> This is the important point. I first encountered this issue when doing
> Lua on top of Java in Android - the userdata could contain really big
> objects like images, but the Lua GC had no way of knowing this. A GC
> on top of a GC! I recall that Eric Wing had similar issues with his
> ObjectiveC/Lua bridge.
>
> I must say I like the idea of deterministic resource finalization,
> although I understand it has runtime implications.
>

As I was writing my initial post I did think about suggesting a
try/finally type approach. But the problem with Lua is that it has
adopted a longjmp based exception handling method ... this makes it
hard / maybe impossible to implement deterministic 'finally' type
clause. When Java code calls C code via JNI, the exception handling is
not done using longjmps.

Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Viacheslav Usov
On Thu, Apr 12, 2018 at 2:35 PM, Dibyendu Majumdar <[hidden email]> wrote:

> As I was writing my initial post I did think about suggesting a try/finally type approach.

I am not exactly sure what you mean here. Are you talking about libraries interacting with Lua?

Or are you talking about some kind of try/finally syntax in Lua, where finally would release resources?

> But the problem with Lua is that it has adopted a longjmp based exception handling method ...

For the record, Lua can be compiled in a mode that relies on C++ exceptions for that.

Cheers,
V.
Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

云风 Cloud Wu
In reply to this post by Dibyendu Majumdar
Dibyendu Majumdar <[hidden email]>于2018年4月12日周四 下午8:32写道:
But of course the AST can be used for other purposes too - for
instance by someone else that needs it ... in that case a different
strategy is necessary. But you are right I could cache the source
string / and AST as a pair - and just rebuild AST from source if
necessary. But I will need to hang on to the source until ... ?


I think source strings are native lua objects, so just use a weak table to cache this pair (string/AST) is ok. Even if you haven't make this cache, you should also wait lua gc to collect source strings.
Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Dibyendu Majumdar
In reply to this post by Viacheslav Usov
On 12 April 2018 at 13:47, Viacheslav Usov <[hidden email]> wrote:

> On Thu, Apr 12, 2018 at 2:35 PM, Dibyendu Majumdar <[hidden email]>
> wrote:
>
>> As I was writing my initial post I did think about suggesting a
>> try/finally type approach.
>
> I am not exactly sure what you mean here. Are you talking about libraries
> interacting with Lua?
>
> Or are you talking about some kind of try/finally syntax in Lua, where
> finally would release resources?

Latter.

>
>> But the problem with Lua is that it has adopted a longjmp based exception
>> handling method ...
>
> For the record, Lua can be compiled in a mode that relies on C++ exceptions
> for that.
>

Actually in the early C++ days, people did implement the invocation of
destructors in a framework that used longjmp/setjmp. If I recall
correctly MFC libraries used various macros to emulate this. The
destructors used to be added to the currently active TRY context
linked list and the code invoked the destructors when an exception was
caught. So perhaps it is possible although with a dyanamic language
like Lua you don't necessarily know which objects have a __gc
metamethod. In Lua you probably need a C api to explicitly add the
object to the current protected call context / finalization list - and
then maybe in the protected call api any such objects can be finalised
in a deterministic manner.

Regards
Dibyendu

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Viacheslav Usov
On Thu, Apr 12, 2018 at 5:31 PM, Dibyendu Majumdar <[hidden email]> wrote:
> Or are you talking about some kind of try/finally syntax in Lua, where
> finally would release resources?

Latter.

I do not think this or that syntax should be impossible because of an implementation detail; the Lua authors control all the details, so I am pretty sure they could find a way.

However, in the earlier threads that I mentioned I made another (but fairly similar) proposal for a syntax where the user would have to request this kind of explicit finalization, which would also be scoped.

I have since come to the conclusion that this will not solve the problem in a good way, because the user of a "costly" userdatum might be ignorant of the need to do so. What I prefer is a way to mark a userdatum in that way independently of scoping, and this should be available to library writers (at a minimum; I am yet to make up my mind about users).

Cheers,
V.

Reply | Threaded
Open this post in threaded view
|

Re: GC and userdata objects

Roberto Ierusalimschy
In reply to this post by Dibyendu Majumdar
> Hi. I have tried using GC step before but did not quite succeed in
> resolving similar issues but that could have been due to my lack of
> understanding. Is there an example of how it should be used?

Memory managed by Lua:

  void *u = lua_newuserdata(L, Nbytes);
  /* no other action required; Lua already knows about the allocation */


Memory managed outside Lua:

  void **u = lua_newuserdata(L, sizeof(void*));

  /* code to set metatable with __gc for the new userdata */
  luaL_getmetatable(L, whatever);
  lua_setmetatable(L, -2);

  /* allocate memory for the new userdata */
  *u = malloc(Nbytes);
  if (*u == NULL) error ...;

  /* Lua does not know about the allocation */
  lua_gc(L, LUA_GCSTEP, Nbytes / 1024);  /* tell Lua about it */

-- Roberto