BIG THANKS to @Dan Pasco for sharing this example: 

The test is simple - I am running in the 2020R1 instance, namespace USER and there are no classes runnable in this namespace - just a clean, new install. I have several other instances running, all different versions/build numbers. I am testing against my XDBC instance which is currently labeled as 2020.4 (obviously not yet released but the IRIS Native code is essentially the same as in 2020.1). The XDBC instance is listening on port 51780. First, proof this doesn't work locally.

USER>write ##class(Sample.Person).CurrentAge($h-35000)                 

WRITE ##CLASS(Sample.Person).CurrentAge($H-35000)
^
<CLASS DOES NOT EXIST> *Sample.Person

And then, attempt the same function using the IRIS Native connection to the XDBC instance.

USER>set host="localhost",port=51780,namespace="USER",user="_SYSTEM",pwd="SYS" 

USER>set connection = ##class(%Net.DB.DataSource).CreateConnection(host, port, namespace, user, pwd)

USER>set iris = connection.CreateIris()                                                             

USER>write iris.ClassMethodValue("Sample.Person","CurrentAge",$h-35000)                             
95

Thank you @Dan Pasco 
I just did a summary on the Class Ref.
I'll take a copy of your reply as an example for the audience.

Article
Robert Cemper · Sep 14, 2020 3m read
IRIS Native API for ObjectScript

It seems to me that for some reason this didn't make its way to the official documentation
and seems to be rather unknown though implemented already in IRIS 2020.1

Thanks to @Dan Pasco I got a hint on the classes involved.
I used the recommended sequence of how to use it. 
it is all directly taken from Class Reference and I just collected it to create a first overview.

50
2 1 153

Hi, @Dan Pasco 
Since what release does this exist.?
It never crossed my way and obviously nobody else reacted to these questions.

I found a  first trace showing up in Class References of 2020.1 but it is even invisible in IRISlatest docbooks.
Big THANKS for uncovering this secret. yes

Thank you @Dan Pasco !
This confirms that it is not just a crazy idea ( as some of my former colleagues classified it).
I met this during testing about a decade ago to manage test data and to compare the impact of code changes. 
And - based on my history - the global was and is the ultimate truth for me of what is done on objects.
Robert

$STORAGE is a special variable which contains the number of bytes available for a current process. After it hits zero you get STORE error. You can profile your code to see which part leaks memory. Small example:

write $STORAGE
>268326112
set i=1
write $STORAGE
>268326104
kill i
write $STORAGE
>268326112

Check that:

  • $storage before and after EvaluateClone call is the same
  • $storage on each loop cycle is the same

Another approach is throwing/catching and logging an exception and checking what's available in each frame.

do clone.Children.RemoveAt(key) // update clone

RemoveAt returns oref, maybe you need to kill it.

I'm wondering if IS has a java-like garbage collector (as in "the object is automatically destroyed"), and if so, is this keeping up with the clone construction and killing, over a 200k+ loop? Is it an immediate thing, or is there a garbage collector crawling around looking for un-referenced objects?

InterSystems products have automatic garbage collector. I'm not sure on the specifics. Calling @Dan Pasco.

I don't think there is a way to do that.

If you check generated code for this procedure it  would be something like this:

try {
  new err, result
  set result=##class(Test.Obj).MyProc()
  return result
} catch err {
  set SQLCODE=-149
  set %msg="SQL Function TEST.OBJ_MYPROC failed with error:  SQLCODE="_err.AsSQLCODE()_",%msg="_err.AsSQLMessage()
  ztrap "SQER"
}

Maybe @Dan Pasco knows?

Hi Rupert, 

I hope you are well and enjoying robotics. 

Thanks for coming back to me.

Your recommended approach, though hugely appreciated, won't work for me I'm afraid. 

In answer to your questions:

  • I am making my call from object script, as part of an already built component within a financial cache application that explicitly uses a SELECT statement
  • Changing this component to use CALL instead of a SELECT is not possible due to dependencies and legacy code.

Essentially question I am asking is why some stored procedures can only be called via CALL and why can some be called by both CALL and SELECT (such as in the case of class queries)?

I get a feeling this has something to do with SQL-Invokable-Routine (SIR) - see @Dan Pasco comment here but there is almost nothing in the documentation or any code for me to go further on.