Resize pictures

This forum is meant for examples of X# code.

User avatar
Chris
Posts: 4948
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Resize pictures

Post by Chris »

^^^ It will be automatically destroyed, when the Garbage Collector decides to kick in, to be precise, and this could potentially take a lot of time. Manually destroying objects that are not longer needed releases the resources they use right now and frees memory more quickly.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
FFF
Posts: 1589
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Resize pictures

Post by FFF »

 Agreed. But, OTOH, memory in itself isn't a value, as long as it is not in use - the GC will and has to kick in, if there's some threshold crossed.
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
Chris
Posts: 4948
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Resize pictures

Post by Chris »

It could be unmanaged resources though, which the GC has no idea that they are related to the managed objects. A very small and simple .Net object could have opened a thousand unmanaged file handles, allocate large chunks of unmanaged memory with Win32 API that the GC has no idea about. Especially in a tight loop, as in the sample below, you can end up allocating too many resources, bringing the PC to a crawl, before the GC suddenly kicks in and tries to release everything (collect all objects created) in one go:

Code: Select all

FUNCTION Start() AS VOID
LOCAL oObject AS SimpleClass
FOR LOCAL n := 1 AS INT UPTO 1000
    oObject := SimpleClass{n}
    oObject: DoSomeWorkWithIt()
    // all of the above 1000 objects are still alive in memory, until the GC kills them all together
NEXT

CLASS SimpleClass IMPLEMENTS IDisposable
    CONSTRUCTOR(nID AS INT)
        SELF:AllocateHugeAmountOfResources()
    DESTRUCTOR()
        SELF: Dispose()
    METHOD Dispose() AS VOID
        SELF:FreeHugeAmountOfResources()
END CLASS

But if you added a oObject: Dispose() at the end of each iteration, resources would be released exactly at the time they are not needed anymore and memory consumption would always remain at the minimum possible.

Even worse, such an object could still be referenced accidentally while is not needed anymore, thus making it impossible to collect it:

Code: Select all

FUNCTION Start() AS VOID
LOCAL oObject1,oObject2 AS SimpleClass
oObject1 := SimpleClass{1}
oObject2 := SimpleClass{2}
oObject1:DoSomeWorkWithIt()
oObject2:DoSomeWorkWithIt()

//
// <a lot of other code in here, having nothing to do with those objects>
//

// this line causes those two objects and their resources
// to still be kept alive till here by the Garbage Collector

? oObject1:GetResult() + oObject2:GetResult()

But, by using BEGIN USING,instead you can limit the visibility of the variables to only where they are really needed, force yourself to write more efficient code and make resource management much more efficient:

Code: Select all

FUNCTION Start() AS VOID
LOCAL nResult AS INT
BEGIN USING VAR oObject1 := SimpleClass{1}
    oObject1:DoSomeWorkWithIt()
    nResult := oObject1:GetResult()
END USING
// The resources used by oObject1 have already been freed here, because
// Dispose() was automatically called from the BEGIN...END USING statement
// Also oObject1 is not visible here anymore, so it can not be used accidentally
// and the GC can also completely collect it

BEGIN USING VAR oObject2 := SimpleClass{2}
    oObject2:DoSomeWorkWithIt()
    nResult += oObject2:GetResult()
END USING
// same for oObject2, it can be collected now

// <a lot of other code in here, having nothing to do with those objects>

When BEGIN USING is used this way, it both limits the scope of the variable and also ensures Dispose() is automatically called, leading to the more "clean" results.

Btw, I am a bit bothered you cannot use my favorite "LOCAL" syntax for BEGIN USING above, will log this.
 
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
Horst
Posts: 337
Joined: Tue Oct 13, 2015 3:27 pm

Resize pictures

Post by Horst »

Hi Chris
To be sure.
So this Code is not right ? BTW it works (the Local)

LOCAL oGraphics        AS Graphics
BEGIN USING oGraphics     := Graphics.FromImage(oNewImage)
       oGraphics:DrawImage(oImage, 0, 0, newWidth, newHeight)
END USING

I have to change it to Begin Using VAR oGraphics and removing the LOCAL Statement    ?

And:
        // PersonenIndex erstellen
        cFilename := SELF:gdbPerson
        odb := _cryptserver {WorkDir () + SELF:cPathRoot+cFilename,TRUE,FALSE,"DBFCDX"}
        // 1
            lOrderOk := odb:createorder (    "Id_Person",,"Id_Person"    ,{|| _Field->Id_Person}    ,    FALSE,FALSE)
                IF  ! lOrderOk ; SELF:WriteCgiLog ("Person, Id_Person Index ERROR !") ; ENDIF
-------> Hier i have to insert the ODB:DISPOSE () ??  <-------------------------
        // Personen Zusatz Index erstellen
        cFilename := SELF:gdbpZusatz
        odb := _cryptserver {WorkDir () + SELF:cPathRoot+cFilename,TRUE,FALSE,"DBFCDX"}
        // 1
            lOrderOk := odb:createorder (    "Id_Person",,"Id_Person+Nachname"    ,{|| _Field->Id_Person+_Field->Nachname}    ,    FALSE,FALSE)
                IF  ! lOrderOk ; SELF:WriteCgiLog ("pZusatz, Id_Person Index ERROR !") ; ENDIF

Horst




 
User avatar
Chris
Posts: 4948
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Resize pictures

Post by Chris »

Hi Horst,

The code with LOCAL and BEGIN USING is still right, only "issue" is that you can still (accidentally) use the oGraphics var even after the END USING line, where the object has been disposed, so does not make sense reusing it (except if you instantiate it again). If you use the VAR syntax, the variable wil be visible only inside the BEGIN...END construct, attempting to use it below will lead to a compiler error, thus preventing possible programmer mistakes.

About using Dispose() in your second sample, it's just good programming practice. In this specific sample, I doubt it will make a noticeable difference, it's just considered a good habbit disposing objects that you no longer needed. If you are used to doing it even in places where it is not really important, you will be doing it also in places where it can make a big difference.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
Horst
Posts: 337
Joined: Tue Oct 13, 2015 3:27 pm

Resize pictures

Post by Horst »

Hi Chris

But it looks like DbServer has no Dispose()  Methode, cant compile.

Horst
User avatar
Chris
Posts: 4948
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Resize pictures

Post by Chris »

Hi Horst,

Not every class has a Dispose() method, it's only those that do allocate external resources and it makes sense to free them ASAP that have it. DBServer's only external resource it uses is to open a dbf file, and there's already a method to dispose that resource, it's just called Close() instead of Dispose() and of course you are most probably calling it already.

The DBServer class was designed more than 25 years ago, when we did not have IDispose interfaces etc, but Close() is indeed working very similarly to Dispose(), in the sense you can also omit calling it, and just let the object go out of scope, wait until the GC collects it, which then calls it Destructor() (Axit() in VO), which in turn calls Close() in order to close the assosiated file. It would just take more time to close the file this way, until there are zero references to this DBServer object anymore and the GC kicks in.

Maybe we should actually now introduce a Dispose() method in DBServer class, which would simply call Close(), to make it more modern and allow DBServer to be used with BEGIN...END USING.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
Horst
Posts: 337
Joined: Tue Oct 13, 2015 3:27 pm

Resize pictures

Post by Horst »

Hi Chris

I think the Dispose for dbServer is not needed, only a comment in the help file that Close will call Dispose, Close is more readable.

Horst
User avatar
wriedmann
Posts: 3773
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Resize pictures

Post by wriedmann »

Hi Chris,
adding a Dispose() method in the DBServer class makes definitively sense because DBServers can be used also in non-legacy code.
I had tried to build a CoreDBFServer class that does not uses DBServer internally, but have failed.
Maybe we can start that together: a DBFServer class that is usable in non VO code.
Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
User avatar
Chris
Posts: 4948
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Resize pictures

Post by Chris »

Horst: Dispose() and destructor are different things. Dispose() is just a name, by convention classes in .Net that need to release resources implement a method with that name, but it could be any other name really. There is no Dispose() method at all in DBServer, there is only Close(), which is called either by the programmer directly, or by the garbage collector when it collects (destructs) the object (Close() is called from the destructor()).

Wolfgang: What do you mean you failed? Do you mean it was too much work? You could maybe just use the original code of DBServer and adjust it to not use USAULs or other VO types (step by step of course, as it is a lot of code).
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Post Reply