Re: [Oorexx-devel] Memory leak (Re: A question (Re: Ad .local and/or monitor class
On Wed, Jul 20, 2022 at 12:35 PM Rony G. Flatscher wrote: > On 20.07.2022 18:05, Rick McGuire wrote: > > When the instance is terminated, all object references are cleared out in > case there might be a dangling reference to the instance object that might > pin it in memory. This includes .local, which should not be pinned > anywhere. I spent fair about of time recently tracing the termination code > to fix #1734, so I know it's doing what it's supposed to be doing. > > I was going to suggest reverting your overrides before terminating the > instance. A more appropriate way to do that is to call the destination > method with no argument. > > Did that as the first strategy: calling destination until .nil gets > returned and then placing back the destination right before it. > > The monitor maintains a queue of the destination objects, so this will pop > your monitor off of the queue and revert to the previous destination. > > Yes. It is possible however that there are Rexx programmers who may put > another destination on top of it (hence popping the destinations until > getting to the very first one in the first attempts) hence using > destination until .nil got returned. (Then changed the logic to simply > remove the monitor objects from .local by replacing the monitor objects > with new ones that got configured like in the beginning which seems to work > as well.) > > The only other thing to comes to mind is that you are calling the > terminate API on the wrong thread or in the wrong circumstances (e.g., on > the correct thread but as the result of a callout from running oorexx > code). I know you tried doing the second one once before. Are you checking > the you are getting a true return value from the terminate call? > > Terminate() is defined to be void (from rexxapi.pdf): > > 1.17.187. Terminate > This API is available in context Instance. > // Method Syntax Form(s) > context->Terminate(); > Terminates the current Rexx interpreter instance. Terminate() may only be > called from the thread context that originally created the interpreter > instance. This call will wait for all threads to complete processing before > returning. > Arguments > None. > Returns > Void. > > When an instance gets created on the native side it will be stored in a > structure together with its Java peer (a jobj) and the Java object > representing the Rexx interpreter configuration used when creating the > instance (a jobj). That structure then gets placed on a simply linked list > on the native side. The Java side gets the context instance pointer > returned as a string rendering. > > In the use case of termination the native side gets that string, turns it > into a RexxInstance pointer and searches it in the linked list. If found it > gets used for invoking Terminate(), if not, then a Java exception gets > raised. > > At operating system thread in which Terminate() gets invoked: this is > controlled by Java (in this case by the cleaner of PhantomReferences). > That is probably your problem. Terminate() is getting called on the wrong thread so the instances are leaking. I've just been looking at the code, and I believe it should be possible to terminate an instance from another thread as long as there's nothing currently running on the initial thread. Rick > > ---rony > > > ___ > Oorexx-devel mailing list > Oorexx-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/oorexx-devel > ___ Oorexx-devel mailing list Oorexx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/oorexx-devel
Re: [Oorexx-devel] Memory leak (Re: A question (Re: Ad .local and/or monitor class
On 20.07.2022 18:05, Rick McGuire wrote: When the instance is terminated, all object references are cleared out in case there might be a dangling reference to the instance object that might pin it in memory. This includes .local, which should not be pinned anywhere. I spent fair about of time recently tracing the termination code to fix #1734, so I know it's doing what it's supposed to be doing. I was going to suggest reverting your overrides before terminating the instance. A more appropriate way to do that is to call the destination method with no argument. Did that as the first strategy: calling destination until .nil gets returned and then placing back the destination right before it. The monitor maintains a queue of the destination objects, so this will pop your monitor off of the queue and revert to the previous destination. Yes. It is possible however that there are Rexx programmers who may put another destination on top of it (hence popping the destinations until getting to the very first one in the first attempts) hence using destination until .nil got returned. (Then changed the logic to simply remove the monitor objects from .local by replacing the monitor objects with new ones that got configured like in the beginning which seems to work as well.) The only other thing to comes to mind is that you are calling the terminate API on the wrong thread or in the wrong circumstances (e.g., on the correct thread but as the result of a callout from running oorexx code). I know you tried doing the second one once before. Are you checking the you are getting a true return value from the terminate call? Terminate() is defined to be void (from rexxapi.pdf): 1.17.187. Terminate This API is available in context Instance. // Method Syntax Form(s) context->Terminate(); Terminates the current Rexx interpreter instance. Terminate() may only be called from the thread context that originally created the interpreter instance. This call will wait for all threads to complete processing before returning. Arguments None. Returns Void. When an instance gets created on the native side it will be stored in a structure together with its Java peer (a jobj) and the Java object representing the Rexx interpreter configuration used when creating the instance (a jobj). That structure then gets placed on a simply linked list on the native side. The Java side gets the context instance pointer returned as a string rendering. In the use case of termination the native side gets that string, turns it into a RexxInstance pointer and searches it in the linked list. If found it gets used for invoking Terminate(), if not, then a Java exception gets raised. At operating system thread in which Terminate() gets invoked: this is controlled by Java (in this case by the cleaner of PhantomReferences). ---rony ___ Oorexx-devel mailing list Oorexx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/oorexx-devel
Re: [Oorexx-devel] Memory leak (Re: A question (Re: Ad .local and/or monitor class
When the instance is terminated, all object references are cleared out in case there might be a dangling reference to the instance object that might pin it in memory. This includes .local, which should not be pinned anywhere. I spent fair about of time recently tracing the termination code to fix #1734, so I know it's doing what it's supposed to be doing. I was going to suggest reverting your overrides before terminating the instance. A more appropriate way to do that is to call the destination method with no argument. The monitor maintains a queue of the destination objects, so this will pop your monitor off of the queue and revert to the previous destination. The only other thing to comes to mind is that you are calling the terminate API on the wrong thread or in the wrong circumstances (e.g., on the correct thread but as the result of a callout from running oorexx code). I know you tried doing the second one once before. Are you checking the you are getting a true return value from the terminate call? Rick On Wed, Jul 20, 2022 at 11:04 AM Rony G. Flatscher wrote: > After further excessive (literally additional days of) debugging I can > state that upon Rexx interpreter instance (RII) terminations using the > native Terminate() API, ooRexx does not release/run the uninit methods of > at least the monitored objects (ooRexx BSF objects) of the .local monitors! > > = > Here the relevant parts after creating, using and Terminate() 1,000 RIIs: > > References from ooRexx objects (under the control of ooRexx), pinning Java > objects: > > ... cut ...*RexxAnalyzeRegistry category=[[B]**- > RefCount: entries=[ **2 000**] | references: min=[1] max=[1] > avg=[ 1,0] sum=[2 000]**- MemSize: entries=[ 2 > 000] | references: min=[1] max=[1] avg=[ 1,0] sum=[ > **2 000**]**RexxAnalyzeRegistry category=[[C]**- > RefCount: entries=[ **2 000**] | references: min=[1] max=[1] > avg=[ 1,0] sum=[2 000]**- MemSize: entries=[ 2 > 000] | references: min=[2] max=[2] avg=[ 2,0] sum=[ > **4 000**]**RexxAnalyzeRegistry category=[[I]**- > RefCount: entries=[ **4 000**] | references: min=[1] max=[1] > avg=[ 1,0] sum=[4 000]**- MemSize: entries=[ 4 > 000] | references: min=[4] max=[4] avg=[ 4,0] sum=[ > **16 000**]**RexxAnalyzeRegistry category=[java.io.InputStreamReader]** > - RefCount: entries=[ **1 000**] | references: min=[1] > max=[1] avg=[ 1,0] sum=[1 000]**RexxAnalyzeRegistry > category=[java.io.PrintWriter]**- RefCount: entries=[ > **2 000**] | references: min=[1] max=[1] avg=[ 1,0] > sum=[2 000]* > > ... cut ... > > References from Java objects (under the control of BSF4ooRexx), pinning > ooRexx engines (REXX_ENGINE, REXX_SCRIPT_ENGINE) and ooRexx objects > (REXX_PROXY): > > > org.rexxla.bsf.engines.rexx.RexxCleanupRef: > RexxCleanupRef [2022-07-20 15:16:10.61300] > RefKind: Instances: Finalized: Not Yet Finalized: > -- > [TEST]..: [ 0] [ 0] [ 0] > [REXX_ENGINE]...: [ 1 002] [ 1 000] [ > 2][REXX_PROXY]: [ 9 000] [ 8 994] [ > 6] > [REXX_SCRIPT_ENGINE]: [ 1 000] [ 999] [ 1] > -- > Totals..: [ 11 002] [ 10 993] [ 9] > > --- > > Here the ooRexx objects referenced by BSF4ooRexx on the Java side (cf. > REXX_PROXY above): > > OREXX_REGISTRY related: > OREXX_REGISTRY ~items: 6 > OREXX_REGISTRY_REFCOUNTER~items: 6 > > t_jsr223.rex: ooRexx 5.0.0 r12473 (17 Jul 2022) / BSF 641.20220717 / Java > 17.0.3.1 (released: 2022-04-22), 64-bit (amd64) / Windows 10.0.19043 > > count_rexx_gc: [4] count_java_gc: [4] > > ... cut ... > > = > Here the relevant parts after creating, using and Terminate() 1,000 RIIs, > this time running the following Rexx program right before termination of a > RII from the Java side: > > ... cut ... >* String rexxCode = // reset .input, .output, .error, > .traceOutput, .debugInput to free BSF objects if any > " lDir = .local -- get .local ;\n" > + > " lDir['INPUT' ]= .monitor~new(ldir['STDIN' ]) ;\n" > + > " lDir['OUTPUT' ]= .monitor~new(ldir['STDOUT']) ;\n" > + > " lDir['ERROR' ]= .monitor~new(ldir['STDERR']) ;\n" > + > " lDir['TRA
[Oorexx-devel] Memory leak (Re: A question (Re: Ad .local and/or monitor class
After further excessive (literally additional days of) debugging I can state that upon Rexx interpreter instance (RII) terminations using the native Terminate() API, ooRexx does not release/run the uninit methods of at least the monitored objects (ooRexx BSF objects) of the .local monitors! = Here the relevant parts after creating, using and Terminate() 1,000 RIIs: References from ooRexx objects (under the control of ooRexx), pinning Java objects: ... cut ... /RexxAnalyzeRegistry category=[[B]- RefCount: entries=[ //*2 000*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 2 000]- MemSize: entries=[ 2 000] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ //*2 000*//]//RexxAnalyzeRegistry category=[[C]- RefCount: entries=[ //*2 000*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 2 000]- MemSize: entries=[ 2 000] | references: min=[ 2] max=[ 2] avg=[ 2,0] sum=[ //*4 000*//]//RexxAnalyzeRegistry category=[[I]- RefCount: entries=[ //*4 000*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 4 000]- MemSize: entries=[ 4 000] | references: min=[ 4] max=[ 4] avg=[ 4,0] sum=[ //*16 000*//]//RexxAnalyzeRegistry category=[java.io.InputStreamReader]- RefCount: entries=[ //*1 000*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 1 000]//RexxAnalyzeRegistry category=[java.io.PrintWriter]- RefCount: entries=[ //*2 000*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 2 000]/ ... cut ... References from Java objects (under the control of BSF4ooRexx), pinning ooRexx engines (REXX_ENGINE, REXX_SCRIPT_ENGINE) and ooRexx objects (REXX_PROXY): org.rexxla.bsf.engines.rexx.RexxCleanupRef: RexxCleanupRef [2022-07-20 15:16:10.61300] RefKind: Instances: Finalized: Not Yet Finalized: -- [TEST]..: [ 0] [ 0] [ 0] [REXX_ENGINE]...: [ 1 002] [ 1 000] [ 2] [REXX_PROXY]: [ 9 000] [ 8 994] [ 6] [REXX_SCRIPT_ENGINE]: [ 1 000] [ 999] [ 1] -- Totals..: [ 11 002] [ 10 993] [ 9] --- Here the ooRexx objects referenced by BSF4ooRexx on the Java side (cf.REXX_PROXY above): OREXX_REGISTRY related: OREXX_REGISTRY ~items: 6 OREXX_REGISTRY_REFCOUNTER~items: 6 t_jsr223.rex: ooRexx 5.0.0 r12473 (17 Jul 2022) / BSF 641.20220717 / Java 17.0.3.1 (released: 2022-04-22), 64-bit (amd64) / Windows 10.0.19043 count_rexx_gc: [4] count_java_gc: [4] ... cut ... = Here the relevant parts after creating, using and Terminate() 1,000 RIIs, this time running the following Rexx program right before termination of a RII from the Java side: ... cut ... /String rexxCode = // reset .input, .output, .error, .traceOutput, .debugInput to free BSF objects if any " lDir = .local -- get .local ;\n" + " lDir['INPUT' ]= .monitor~new(ldir['STDIN' ]) ;\n" + " lDir['OUTPUT' ]= .monitor~new(ldir['STDOUT']) ;\n" + " lDir['ERROR' ]= .monitor~new(ldir['STDERR']) ;\n" + " lDir['TRACEOUTPUT']= .monitor~new(ldir['ERROR' ]) ;\n" + " lDir['DEBUGINPUT' ]= .monitor~new(ldir['INPUT' ]) ;\n" ; / Object obj=null; try { obj=rexxInterface.jniRexxRunProgram( rii_ID, // RexxInstance instance ID 2, // invocationType, // determines whether "CallProgram", "LoadPackage" or "LoadPackageFromData" is to be used "RexxEngine_terminate_cleanup_local_monitors" , // fileName /rexxCode /, // Rexx code to execute null// arguments ); } ... cut ... res=rexxInterface.jniRexxTerminateInterpreterInstance(rii_ID); // wait for termination of all Rexx threads of this interpreter instance ... cut ... Doing this for every RII will cause the UNINIT method of the BSF objects to be run, correctly removing the pinned (peer) Java objects: References from ooRexx, pinning Java objects: ... cut ... /RexxAnalyzeRegistry category=[[B]- RefCount: entries=[ //*2*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 2]- MemSize: entries=[ 2] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ //*2*//]//RexxAnalyzeRegistry category=[[C]- RefCount: entries=[ //*2*//] | references: min=[ 1] max=[ 1] avg=[ 1,0] sum=[ 2]- MemSize: entries=[ 2] | references: min=[ 2] max=[ 2] avg=[ 2,0] sum=[ //
Re: [Oorexx-devel] Ad debugging garbage collection dependent resources
Dear P.O., On 20.07.2022 08:25, ooRexx wrote: I assume this functionality can not be made a library (like orxutils et al) but need to be included in the build process, right? IF not included/accepted in the trunk I suggest you put all the necessary files in an entry under tools and document how to make it work. In this way anyone who wants to give it a try can do so without affecting the official builds. Well, this would be some special location then, like "unaccepted, but useful" features and I would doubt that this was really helpful for anyone as a person needs to be aware of that. It is better not to make things more complicated. As someone pointed out this seems to be a substantial effort and we should keep it in the SVN repository. Well there was some effort to figure it out. The patch is actually rather short and simple. Maybe I do the same as for the concurrent trace and file a RFE with the patch. This way the RFE is documented and whoever needs the patch can fetch and apply it from there. There are situations (mostly debugging, but not only), where gc() is important, also for ooRexx. ---rony On 16. Jul 2022, at 17:11, Rony G. Flatscher wrote: While debugging (for almost two months) ooRexx and BSF4ooRexx/Java it was almost impossible to determine the source of some memory leaks. The reason was not being able to rely on the garbage collector having run at a specific point in time such that all objects that have no references anymore get garbage collected and if uninit methods present have them run. Only then would an analysis become really possible. As it is possible with Java to kick the Java garbage collector in order to get at a stable state with regards to garbage collecting Java objects a gc() for ooRexx would make it possible to arrive at the same ability for ooRexx: to become able to arrive at a stable state at a certain point in time. Only then would it become possible to assess where the sources of memory leaks are rooted: on the ooRexx side or on the Java side. Consider also the runtime dynamics in this context: there are cross calls/invocations, even on different ooRexx and on different Java threads at the same time (which all need to be controlled/synchronized while debugging). In the end I created a gc() built in function in my "personal ooRexx interpreter" ;) to become able to debug ooRexx and Java/BSF4ooRexx to help identify all such locations and determine which side is responsible for keeping references that should have been freed. While debugging quite a lot of programs got created that would excercise gc() and java.lang.System.gc() allowing to determine the various sources of memory leaks (ooRexx and Java side) and thereby becoming able to address them and test the solutions. Again, without gc on the ooRexx side this would not have been possible! --- For everyone who has such a need (to debug complex interactions with external function libraries with observed memory leaks) it is mandatory to have the ability in ooRexx available to kick off the ooRexx garbage collector to achieve a stable state. Therefore proposing to add a gc() BIF (built-in-function) to ooRexx to enable debugging in such dynamic native peer systems/environments. As any garbage collector run is relatively expensive it is necessary to warn any ooRexx programmer from using the gc() BIF in regular programs and make sure they only consider its use for debugging only. This may be achieved by creating a documentation that communicates this fact clearly/explicitly, maybe something along the lines: "Warning: this function should only be used for developers who explicitly have a very special need of debugging memory leaks with external function libraries! Do not use this function in regular ooRexx applications as each explicit invocation of this function is very expensive and may slow down the execution of your ooRexx application considerably! This function will kick off the ooRexx garbage collector out-of-bounds and finalize all objects that have no references at that particular point in time and run their uninit methods if present. Remark: the ooRexx garbage collector will get invoked by the interpreter at well defined states and will thereby make sure that the programs will execute as efficiently as possible. Therefore there is no need - other than for debugging - to explicitly invoke the garbage collector out-of-bounds." Of course, not being a native English speaker the above draft for the documentation text may need to be rephrased to be more clear. However, it should be clear for everyone that the ooRexx gc() BIF is not meant to be used for production. ---rony ___ Oorexx-devel mailing list Oorexx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/oorexx-devel
[Oorexx-devel] [Oorexx-svn] SF.net SVN: oorexx-code-0:[12476] main/trunk/interpreter
My initial mail bounced so I send it to the developers list This change made one test fail in the SYMBOL testgroup (all platforms I think) failure] 20220720 02:10:59.042803 Test: TEST_SYMBOL Class: SYMBOL.testGroup File: .../ooRexx/base/bif/SYMBOL.testGroup Line: 144 Failed: assertEquals Expected: VAR Actual: LIT This line self~assertEquals('VAR', SYMBOL('J’)) Should probably be changed to self~assertEquals(‘LIT', SYMBOL('J’)) Hälsningar/Regards/Grüsse, ooRexx oor...@jonases.se ___ Oorexx-devel mailing list Oorexx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/oorexx-devel