Notifications
Clear all

[Closed] Memory Leaking When Copying BaseObject

The Heap keeps relatively low, the problem is the system memory.

Doing this operations without calling gc() can eat all the system RAM pretty quick, and we know Max don’t like to run out of RAM.

why do you need many instances created (cloned) in the loop?

try to save file or increase the heapsize. it might release the memory.

what happens… base object is an object which has a dependency connection (invisible probably for mxs (for refs.getdependents)). it makes it ‘lifetime’ for global scope.

only the object exactly knows what kind of dependency there. the system only asks: “do you have any?” the object answers: “yes”. ok … it’s protected. only the object knows how to delete itself in this case.

but when the system is doing some garbage collection it just in case sends all objects something like “maybe a time to delete yourself?”. and some objects see: “indeed! there is nothing to keep me in this world! i wanna die!!! ” . something like that

1 Reply
(@polytools3d)
Joined: 11 months ago

Posts: 0

I don’t need them to be created in a loop. I just coded that example for those who wanted to give it a try. But creating them out of a loop behaves the same.

C’mon, I don’t want soup, I want ice-cream.
– Saving the file does not reclaim the memroy.
– Holding the file does not reclaim the memroy.
– Increasing the Heap size does not reclaim the memroy.

The only way I found is either [b]gc()[/b] or [b]clearundobuffer()[/b] followed by [b]gc light:on[/b] which is basically the same. Clearing the Undo Buffer works, even when there are no (visible) entries of the operation.

Could be. I can’t find any dependents of the copies.

It could be. Even though it is weird why calling cg() after the initial variable creation does solve the problem. The copies are still the same. It looks like a bug in the Garbage Collector or the Undo system.

When you get the chance , give this a try to see what I mean. 
(
    delete objects
    source = converttopoly (geoSphere radius:50 segs:40)
    mesh = copy source
    gc()
    
    sh = heapfree; ram = (sysinfo.getMAXMemoryInfo())[8]
    
    for j = 1 to 25 do (copy mesh.baseobject; gc light:on)
    
    format "heap:%	RAM:% MB
" (sh-heapfree) (((sysinfo.getMAXMemoryInfo())[8]-ram)/1024.^2)
    )
    

heap:264L RAM:4.28125 MB

(
    delete objects
    source = converttopoly (geoSphere radius:50 segs:40)
    gc()
    mesh = copy source
    
    sh = heapfree; ram = (sysinfo.getMAXMemoryInfo())[8]
    
    for j = 1 to 25 do (copy mesh.baseobject; gc light:on)
    
    format "heap:%	RAM:% MB
" (sh-heapfree) (((sysinfo.getMAXMemoryInfo())[8]-ram)/1024.^2)
    )

heap:3376L RAM:205.449 MB

i’ve had a chance to look at max and play with sdk…

here is a mxs extension that fixes (i hope) the leaking issue. as i expected poly base was protected from collection by its dependency to somewhere (i don’t have a time to investigate to what).
so:

def_visible_primitive (deleteReferenceTarget, "deleteReferenceTarget");
Value* deleteReferenceTarget_cf(Value** arg_list, int count)
{
	check_arg_count_with_keys(deleteReferenceTarget, 1, count);
	ReferenceTarget* targ = arg_list[0]->to_reftarg();
	if (targ) 
	{
		BOOL refDeleted = key_arg_or_default(refDeleted, &true_value)->to_bool();
		if (refDeleted)
		{
			targ->DeleteAllRefs();
			targ->DeleteThis();
		}
		else
		{
			targ->MaybeAutoDelete();	
		}
		return bool_value(ReferenceTarget::IsDeleted(targ));
	}
	return &undefined;
}

using:

(
 	gc()	-- Just to not run out of RAM after several tests
 	delete objects
 	
 	poly = converttopoly (geoSphere radius:50 segs:40)
 	mesh = converttomesh (geoSphere radius:50 segs:40)
 	
 -- 	gc() 					-- Uncomment to prevent the memory leaking
 -- 	select #(poly, mesh)	-- Uncomment after previous GC() and the memory leaking is back
 	
 	-- POLY TEST ---------------------------------------------------------------------------
 	st = timestamp(); sh = heapfree
 	usedRAM = (sysinfo.getMAXMemoryInfo())[8]
 	
 	local polyCopy = undefined
 	local meshCopy = undefined
 		
 	with undo off for j = 1 to 200 do
 	(
 		polyCopy = copy poly.baseobject
		deleteReferenceTarget polyCopy
 		--free polyCopy			-- Do nothing
 		--polyCopy = undefined	-- Do nothing
 		--gc light:on delayed:off	-- Do nothing
 	)
 	
 -- 	gc()	-- Uncomment ro reclaim all the used RAM so far
 	usedRAM = (sysinfo.getMAXMemoryInfo())[8] - usedRAM
 	format "POLY >> time:% heap:%	RAM:% MB
" (timestamp()-st) (sh-heapfree) (usedRAM/1024.^2)
 	
 		
 	-- MESH TEST ---------------------------------------------------------------------------
 	st = timestamp(); sh = heapfree
 	usedRAM = (sysinfo.getMAXMemoryInfo())[8]
 	
 	for j = 1 to 200 do
 	(
 		meshCopy = copy mesh.baseobject
 		free meshCopy			-- Do nothing
 		meshCopy = undefined	-- Do nothing
 		--gc light:on delayed:off	-- Do nothing
 	)
 	
 -- 	gc()	-- Uncomment ro reclaim all the used RAM so far
 	usedRAM = (sysinfo.getMAXMemoryInfo())[8] - usedRAM
 	format "MESH >> time:% heap:%	RAM:% MB
" (timestamp()-st) (sh-heapfree) (usedRAM/1024.^2)

	gc light:on 
	completeredraw()
 )

result:

POLY >> time:1968 heap:30320L	RAM:0.0 MB
MESH >> time:222 heap:-62456L	RAM:272.254 MB

Thank you so much for your time Denis!

That’s what I thought doing in the end, a function to release the objects from memory.
Will investigate if I can add an option to the Free() method.

I couldn’t find what the dependencies are, but I suspect they may be related to the controllers, as you can animate a copy of the BaseObject in memory, and if you save it with the scene the animations will remain there.

Haven’t yet tested it, but I think it will work fine.

thank you too. it was very good catch. i used a temp clone of poly object in some of my tools. and i see now that it was leaking. i thought it was because of just a big mesh but as i see now it’s the issue you found (unreleased poly object).
so it’s exactly what this forum is about – help each other

Works great so far.

(
  	 with undo off
  	(
  		delete objects
  		 poly = converttopoly (geoSphere radius:50 segs:40)
  		
  		 ram = (sysinfo.getMAXMemoryInfo())[8]
  
  		polyCopy = copy poly.baseobject
  		format "isDeleted:%	copyHandle:%	%
" (isDeleted polyCopy) (getHandleByAnim polyCopy) polyCopy
  		
  		deleteReferenceTarget polyCopy
  		format "isDeleted:%	copyHandle:%		%
" (isDeleted polyCopy) (getHandleByAnim polyCopy) polyCopy
  
  		format "RAM:% MB
" (((sysinfo.getMAXMemoryInfo())[8]-ram)/1024.^2)
  	)
  	gc light:on -- Needed to avoid memory leacking
   )

– isDeleted:false copyHandle:16839P Editable Poly
– isDeleted:true copyHandle:0 <Deleted Max object>
– RAM:0.0 MB

but when the system is doing some garbage collection it just in case sends all objects something like “maybe a time to delete yourself?”. and some objects see: “indeed! there is nothing to keep me in this world! i wanna die!!! ” . something like that

it’s probably a simple “reference” counter, maintained in the base MaxHeapOperators Class , means object won’t/can’t be deleted unless the counter is zero.

Here are the dependencies:

(
 	delete objects
 	poly = converttopoly (geoSphere segs:6)
 	polyCopy = copy poly.baseobject
 	polyNumRef = refs.getNumRefs polyCopy
 
 	mesh = converttomesh (geoSphere segs:6)
 	meshCopy = copy mesh.baseobject
 	meshNumRef = refs.getNumRefs meshCopy
 
 	format "%
	Dependencies:
" polyCopy
 	for j = 1 to polyNumRef do format "		%
" (refs.getReference polyCopy j)
 
 
 	format "%
	Dependencies:
" meshCopy
 	for j = 1 to meshNumRef do format "		%
" (refs.getReference meshCopy j)	
 )

– Editable Poly
– Dependencies:
– ReferenceTarget:ParamBlock2
– ReferenceTarget:ReferenceTarget

– Editable Mesh
– Dependencies:
– ReferenceTarget:ReferenceTarget

could you print them please? (no max to try right now)

2 Replies
(@miauu)
Joined: 11 months ago

Posts: 0

Editable Poly
	Dependencies:
		ReferenceTarget:ParamBlock2
		ReferenceTarget:MasterPointControlImp
OK

(@polytools3d)
Joined: 11 months ago

Posts: 0

I’ve updated the function and results.

Page 2 / 2