Notifications
Clear all

[Closed] Memory Leaking When Copying BaseObject

There is a very strange memory leaking when copying a node baseObject. The leaking is not that much on the Max heap, but on the system memory, and it seats there until you do a manual Garbage Collection.

To prevent the leaking you can do gc() just after the nodes creation and it will work fine but only until you select the nodes.

This does not happen if you copy the TriMesh value of the baseObject, but I need to copy the baseObject and a full gc() is of no help as it also cleans up the undo buffer.

I’ve basically tried all I can think of, undo, redraw, notifications, but I can’t find any solution other than doing a gc() after each copy. I suppose it might be something related to the controllers yet I have no clue of how to fix this.

It happens in any Max version.

Does anyone know what’s going on and how to prevent it?

(
 	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
 		
 	for j = 1 to 200 do
 	(
 		polyCopy = copy poly.baseobject
 		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)
 )
26 Replies

i can explain at least a difference between making a copy of trimesh and Editable Poly Base.

trimesh is not an object. making it doesn’t cause undo holding.

when you make a poly base it’s going through undo system so it’s more then mxs heap memory.

free method deletes mesh and frees allocated by system memory.
but it does do absolutely nothing for poly base. this object stays in memory and in undo stack.

but a copy of poly base doesn’t have any dependents, so after gc it will be removed by the system using default AutoDelete method.

But it behaves the same for Poly and Mesh BaseObjects.

The undo buffer does not have any entries. Turning it off does not make it any better.

The theHold is of no help. Can’t get anything valuable out of it.

theHold.GetGlobalPutCount() – is the same after and before the copies
theHold.DisableUndo() – Doesn’t help

But the leak seems to be indeed somewhere in the limbo of Undo System. Calling clearundobuffer() and then gc light:on does the same as gc() and releases the memory.

So, where do all these copies go and how can they be cleaned up?

Why doing one Garbage Collection just after the nodes creation eliminates all the memory leak?

Why selecting the nodes after that makes the copy to leak memory again?

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

about an undo.

this is not ‘actual’ undo which you can see in max undo stack and registered with theHold system. it’s a ‘potential’ undo for data that can be associated with an instance of this class

it’s all about how the reference object was created and registered in the system.

i don’t have max to try but…

if you delete some verts of a trimesh copy is it undoable?
try to do the same for a poly base copy.

probably to delete a poly base and clean allocated by it memory we have to delete MNMesh or PolyObject associated with it.

The modifications to a TriMesh object do not go into the undo system.

Modifications to a copy of a BaseObject seems to go into the undo buffer but not the copy operation itself.


  (
   	delete objects
   	
   	base = converttomesh (sphere())
   		
   	gc()
   	format "undo:% put:%
" (theHold.getCurrentUndoLevels()) (theHold.GetGlobalPutCount())
   
   	bo = copy base.baseobject
   	meshop.deletefaces bo #all
   
   	format "undo:% put:%
" (theHold.getCurrentUndoLevels()) (theHold.GetGlobalPutCount())
   		
   	format "numfaces:%
" bo.mesh.numfaces
   	max undo
   	format "numfaces:%
" bo.mesh.numfaces
   
   	format "undo:% put:%
" (theHold.getCurrentUndoLevels()) (theHold.GetGlobalPutCount())
   )

undo:0 put:32
undo:0 put:34
numfaces:0
– Max Undo Here –
numfaces:224
undo:1 put:34

as i understood a modification of trimesh is not undoable, but of a poly base is.

please make another test.

collect all copies of a poly base in a global array, and do gc() after that. does it free the memory as well?

If the copies of the BaseObject are collected in a global array the gc() does not release the memory.

The only way is to free() the array first and then call gc(). But gc light:on wont release the memory.

Even if you collect them several times overwriting the global array (which incrementally leaks memory), once you free the array and call gc() all the memory is reclaimed, or almost all.

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

this is what i was expecting.
i see how to delete a poly base. but i have no idea yet how to force releasing of the memory…

to delete the poly base you have probably just call DeleteAllRefs() method of its RefgerenceTarget

hmm… according to the sdk help the calling DeleteThis() might free the memory.

Thank you Denis for your input.
So you see no way to handle this from MXS?

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

true. it needs something ‘intense’ (like gc) to kick the system. the system has to delete these instances automatically because they don’t have real dependents.
maybe increase heapsize?
:shrug:

another idea save

just curious… do you remember i’ve posted ‘trimesh base’ and ‘splineshape base’ mxs extensions? do they leak the same way as a poly base?

2 Replies
(@polytools3d)
Joined: 11 months ago

Posts: 0

Haven’t tested those, but will see if they can help on this.
This is a weird behavior. Can’t really understand what’s going on.

(@polytools3d)
Joined: 11 months ago

Posts: 0

Ok, I’ve tried this one and there are two situation.

If I use it to create the original object and then just copy it, then it behaves the same as copying the BaseObject and I need to call gc() to free up the memory. The good thing is that it is around 15 times faster and uses 7 times less memory, but it does need a mesh (not a BaseObject) and does not have all the properties of the BaseObject.

If I instead use it inside the loop to create a new ” Editable Mesh”, then the leaked memory seats there and there is no way to get it back, Calling cg() does nothing and the used system RAM keeps growing until there is no more.

Page 1 / 2