Allokation im Speicher von weitergegebenen Variablen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von Dksksm.

    Allokation im Speicher von weitergegebenen Variablen

    Hallo,

    ich habe hier nochmal ein paar theoretisch Überlegungen; man versucht seinen Code ja zu organisieren und kapselt die ein oder anderen Dinge in Methoden und Funktionen.
    Wenn ich Variablen ByRef übergebe, verbrauche ich dann weniger Speicher? Also so hab ich gedacht:

    VB.NET-Quellcode

    1. Dim arr as Integer() = {1,2,3} ' Datenvolumen: 3 Einheiten (arr)
    2. Dim lst = zulist(arr) 'Datenvolumen: 6 Einheiten (lst+arr)
    3. arr = Nothing ' Datenvolumen: 3 Einheiten (lst)
    4. Function zulist(x as Integer()) as List(Of Integer)
    5. Dim hold = x.ToList 'Datenvolumen: 9 Einheiten (hold+x+arr)
    6. Return hold
    7. End Function
    8. '----------------------------
    9. Dim arr as Integer() = {1,2,3} ' Datenvolumen: 3 Einheiten (arr)
    10. Dim lst = zulist(arr) 'Datenvolumen: 6 Einheiten (lst+arr)
    11. arr = Nothing 'Datenvolumen: 3 Einheiten (lst)
    12. Function zulist(ByRef x as Integer()) as List(Of Integer)
    13. Dim hold = x.ToList 'Datenvolumen: 6 Einheiten (hold+ x=arr)
    14. Return hold
    15. End Function

    Kurzfristig braucht die obere Funktion quasi 50% mehr Speicher, weil die Variablen kopiert wird. Auf der anderen Seite finde ich das komisch weil ja ByVal die Default Option ist.

    Viele Grüße

    Haudruferzappeltnoch schrieb:

    Auf der anderen Seite finde ich das komisch weil ja ByVal die Default Option ist.
    Das ist gut so.
    Probiere folgendes aus:

    VB.NET-Quellcode

    1. Sub Test1(ByVal x As Integer())
    2. x = Nothing
    3. End Sub
    4. Sub Test2(ByRef x As Integer())
    5. x = Nothing
    6. End Sub
    7. Sub Test()
    8. Dim x() = New Integer() {1,2,3,4,5} // oder so
    9. Test1(x)
    10. MessageBox.Show(x(2).ToString())
    11. Test2(x)
    12. MessageBox.Show(x(2).ToString())
    13. End Sub
    Und:
    Speicherplatz sollte nicht das Problem sein!
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich glaub, jetzt bist Du bei Micromanagement angekommen. Wenn Du auf so riesige Abarbeitungen angewiesen bist, dass es einen relevanten Performanceunterschied macht, ok, dann kann man über Verbesserungen nachdenken. Aber vorher verschwend zumindest ich keinen Zusatzgedanken daran.
    Mir ist klar, dass das hier erstmal eine theoretische Diskussion ist. Aber die Formulierung deutet darauf hin, dass Du vorhast, dass ggf. in Deine Projekte so einzubauen. ByRef ist immer wieder ein Stolperstein, da es bedeutet, dass die Funktion die potentielle Fähigkeit hätte, das Übergebende zu ändern. Da muss man dann schon mehr Aufmerksamkeit in den Code stecken und noch mehr aufpassen. Eine entsicherte Handgranate mehr im Gepäck.
    Ja, man kann inzwischen Änderungen verhindern:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim x = 1
    3. Foo((x)) 'x BLEIBT 1
    4. Foo(x) 'x wird zu 2
    5. End Sub
    6. Private Sub Foo(ByRef x As Integer)
    7. x = 2
    8. End Sub

    Aber dann wird das Nachdenken über den Code noch stressiger. Irgendwann fragt sich der Leser: »Warum wird da überhaupt ByRef verwendet?«
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Das wäre auch meine Frage, diese Verwendung von ByRef würde ja nur noch Einfluss auf die abgespeicherten Daten nehmen.

    @RodFromGermany Ich weiß das ByRef so funktioniert, daher wäre meine Frage ist es denn standardmäßig so das übergebene Parameter bearbeitet werden? Weil ich mache das jetzt nicht oft.

    Ich frage nicht, weil ich das einbauen möchte. Ich frage weil es mir große Schwierigkeiten bereitet zu verstehen wie der Code Speicher und Prozessor benutzt und ich egal was ich code ich zumindest wissen solllte, was da passiert. Denn wenn es mal ein Problem geben sollte, sei es dass man eine Situation hat wie du sagst, dann muss ich es ja auch sehen können. Und man kann mit rumprobieren ja auch nicht reingucken.

    Vielleicht bin ich da ein bisschen komisch, aber ich kann mir die abstrakte Theorie am Ende besser merken als die Anwendungsbeispiele.

    Umgekehrt ist das für Disposables ja auch noch relevant, ByVal muss man ja zweimal Disposen, in der Methode und danach auch.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Hallo,
    dein Beispiel ist Quatsch. Arrays sind Referenzdatentypen und werden immer "ByRef" übergeben (Das ist wahrscheinlich auch das, was RodFromGermany dir sagen möchte). Wie hast du den Speicherverbrauch in deinem Beispiel ermittelt?

    Ich habe dein Beispiel mal einem MemoryBenchmark unterzogen. Es werden in beiden Fällen 72Byte für das Erstellen der Liste im Speicher allokiert - ob da nun ByRef oder (implizit) ByVal steht.
    Ich habe nichts ermittelt. Ich weiß nicht wirklich wie das geht. Die Liste ist ja in beiden Fällen auch gleich groß
    Wie wäre es denn bei einem normalen Datentyp? Ich habe eigentlich nur ein Array als Beispiel genommen. Dass das ungeeignet ist, habe ich nicht bedacht.
    Ich habs genau vertauscht Integer, Short, usw. sind Structures und Arrays sind Classes.

    Value Types:
    ...
    ...
    All structures, even if their members are reference types
    ...

    Bedeutet dieser Zusatz, dass

    VB.NET-Quellcode

    1. Structure a
    2. Property x as Integer() = {1,2,3}
    3. End Structure
    4. Dim a1 = new a
    5. 'a1.x als ByVal übergeben würde?
    6. Dim a2 = new a
    7. 'a2.x = {3,3,3} oder nur das hier a2.x nicht a1.x ändert, weil das wäre für mich ohnehin eine verpflichtende Eigenschaft


    Wie wirkt denn die Doppelklammer bei Referenzdatentypen? Funktioniert das da nicht?

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    ByRef/ByVal ist keine Entscheidung wegen Speicherplatz oder Performance.
    Hab ich eigentlich mal erwähnt, dass man von Speicher- oder Performance - Optimierungen tunlichst die Finger lassen möge?
    Haupt-Kriterium für Code-Qualität ist Lesbarkeit und Architektur.
    Speicher und Performance kommen 50km weiter hinten.
    Oder pointierter: Wenn man auf Speicherbedarf und Performance stiert, dann ist man dabei, schlechten Code zu schreiben, weil man focussiert nicht mehr aufs Wesentliche.

    Aber zurück zu ByRef: Gibts da übrigens nicht ein Tut im Tut-Bereich, was erklärt, wozu ByRef gut ist?

    Aber ich kanns auch vorsagen:
    Manchmal benötigt man zwei Rückgabe-Werte von einer function.
    Dummerweise bot Function biss vor Kurzem nur einen Return-Value.
    Das ist ziemlich unschön, aber wenn nunmal so ist, dass Du zwei ReturnWerte von einer Function brauchst - dann bleibt kaum etwas anderes übrig, als den einen Rückgabewert sich indirekt zu holen, als ByRef-Parameter.
    Als Beispiel guck dir die Integer.TryParse()-Methode an.
    Da braucht man 2 ergebnisse: 1) ob das Parsen geklappt hat, 2) der ausgeparste Wert.
    Oder Double.TryParse. Oder Dictionsary(Of Tkey, TValue).TryGetValue(). usw.
    Also guck dir das wirklich an.
    Neuerdings kennt .Net auch Value-Tuple, zumindest c#.
    Damit kann eine Methode mehrere Werte returnen, und ByRef ist hofflich Geschichte.
    Und was passiert wenn ich eine Function (also deren Return-Value) einer anderen Function als Parameter übergebe?

    Ist da dann eine Variable außerhalb beider Functions im Speicher?

    Bsp.:

    VB.NET-Quellcode

    1. Function test1() as SqlCommand
    2. Using cmd as New SqlCommand("","")
    3. Return cmd
    4. End Using
    5. End Function
    6. Function test2(cmd as SqlCommand) as Integer
    7. ...
    8. cmd.Dispose
    9. Return 0
    10. End Function
    11. Dim a = test2(test1) 'bleibt der Returned Wert test1 hier undisposed zurück?

    Haudruferzappeltnoch schrieb:

    bleibt der Returned Wert test1 hier undisposed zurück
    In nativem .Net-Code bleibt nichts undisposed zurück.
    Dafür gibt's den Garbage Collector.
    Mach dir weniger Sorgen um solche Dinge, solange du nicht echte Probleme in der Praxis damit hast.

    Allenfalls wenn du Klassen instanziierst, die IDisposable implementiert haben, kannst du diese im Using-Block halten.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „petaod“ ()

    Haudruferzappeltnoch schrieb:

    wenn ich eine Function (also deren Return-Value) einer anderen Function als Parameter übergebe? - Ist da dann eine Variable außerhalb beider Functions im Speicher?
    nein.

    Allerdings der gezeigte Code wird Probleme machen, weil der Rückgabewert von test1() ist bereits disposed, wenn er dem Aufrufer returnt wird.
    Und um disposable Objekte (wie etwa dbCommands) musste dir sehr wohl Sorgen machen, damit die nicht undisposed zurückbleiben.

    Haudruferzappeltnoch schrieb:

    VB.NET-Quellcode

    1. Dim a = test2(test1) 'bleibt der Returned Wert test1 hier undisposed zurück?
    Mach einen Test mit einer im Debugger lesbaren Instanz (nicht unbedingt SqlCommand) und teste das aus.
    Stell nur dann eine Frage, wenn Du das Ergebnis nicht verstehst. ;)
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich weiß nicht welche Objekte Disposable sind und "lesbar".

    ErfinderDesRades schrieb:


    weil der Rückgabewert von test1() ist bereits disposed, wenn er dem Aufrufer returnt wird

    Ich dachte das wäre der Sinn davon, dass Using in der Lage ist noch nach dem Return zu Disposen.

    Aber wenn das so ist, heißt das doch man darf gar keine Disposable Objekte in einer Function returnen. Sonst werden die ja nie Disposed

    ErfinderDesRades schrieb:

    Allerdings der gezeigte Code wird Probleme machen, weil der Rückgabewert von test1() ist bereits disposed, wenn er dem Aufrufer returnt wird
    Der wird an test2 zurückgegeben und dort verarbeitet.
    Außerhalb davon interessiert ja auch nur noch der Rückgabewert von test2.

    Haudruferzappeltnoch schrieb:

    Aber wenn das so ist, heißt das doch man darf gar keine Disposable Objekte in einer Function returnen
    Darfst du schon.
    Du kannst innerhalb der Funktion ein Objekt erzeugen und das zurückgeben.
    Das lebt halt so lange bis du es selbst disposed.
    Oder eben der Garbage Collector es vernichtet, weil er merkt, dass es nicht mehr benötigt wird, weil die Variable, an die du das Objekt zugewiesen hast sich nicht mehr im Gültigkeitsbereich befindet.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Haudruferzappeltnoch schrieb:

    Ich weiß nicht welche Objekte Disposable sind und "lesbar".
    So was:

    VB.NET-Quellcode

    1. Public NotInheritable Class TestClass
    2. Implements IDisposable
    3. Public TestString As String
    4. Public Sub New(Test As String)
    5. Me.TestString = Test
    6. End Sub
    7. Public Sub Dispose() Implements IDisposable.Dispose
    8. Me.TestString = String.Empty
    9. End Sub
    10. End Class
    Aufruf so:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    3. Using test = New TestClass("Test")
    4. Debugger.Break()
    5. End Using
    6. End Sub
    7. End Class
    Modifiziere das so, dass es mit Deinem Code läuft und sieh Dir die Variable TestString mit Haltepunkten an.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Haudruferzappeltnoch Merkt das der Compiler oder erst die Laufzeit?
    Poste mal Deinen aktuellen Code.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Haudruferzappeltnoch schrieb:

    Ich habe aber ja gerade festgestellt, dass das Objekt, das in der Funktion erzeugt wird nicht disposed werden kann, wenn man es noch zurückgeben möchte.
    Ich glaube, du meinst ein bischen was anderes:

    Vermutung' schrieb:

    Ich habe aber ja gerade festgestellt, dass das Objekt, das in der Funktion erzeugt wird nicht disposed werden darf, wenn man es noch zurückgeben möchte.
    Jo - isso.
    Ich bekomme eine Exception, das ist Laufzeit denke ich? Also der Code vorher wird ausgeführt.

    Das hier hab ich jetzt, aber da wird jetz nicht mehr Disposed, wo ich es eigentlich wollte bzw. läufts jetzt anders:

    VB.NET-Quellcode

    1. Private Sub PreparePreview() 'GP habe ich jetzt auf dem Form deklariert, damit ich es immer Disposen kann, deswegen füllt diese Methode GP
    2. 'wenn ein neuer GP kommt brauche ich den alten nicht mehr, deswegen wollte ich den GP eigentlich lokaler deklarieren als im Form, aber dann kann ich nicht Disposen
    3. If GP IsNot Nothing Then GP.Dispose()
    4. GP = GetPath()
    5. End Sub
    6. Private Function GetPath() As GraphicsPath
    7. 'Using GPath as New GraphicsPath '(so mit Fehler)
    8. Dim GPath As New GraphicsPath
    9. ...
    10. Return GPath
    11. 'End Using '(so mit Fehler)
    12. End Function
    13. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    14. e.Graphics.DrawPath(_pen, GP)
    15. 'e.Graphics.DrawPath(_pen, GetPath) 'ohne Dispose
    16. End Sub

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    'Using GPath as New GraphicsPath '(so mit Fehler)
    Logisch.
    Wenn du das Objekt noch in der Function weg wirfst, ist es danach nicht mehr verfügbar.
    Wenn du es nach außen durchreichen willst, darfst du es natürlich nicht in einen Using-Block nehmen.

    Haudruferzappeltnoch schrieb:

    'wenn ein neuer GP kommt brauche ich den alten nicht mehr, deswegen wollte ich den GP eigentlich lokaler deklarieren als im Form, aber dann kann ich nicht Disposen
    Ich verstehe nicht, warum du unbedingt von Hand disposen willst.
    Da haben sich die Compilerentwickler sehr viele Gedanken gemacht, damit das automatisch geht.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    @Haudruferzappeltnoch Deine Herangehensweise ist suboptimal.
    Erstelle nur eine Instanz von GraphicsPath in Deiner Klasse.
    Ersetze in Deinem Code GPath.Dispose() und New GraphicsPath durch GPath.Reset().
    Danach erstelle den Pfad.
    docs.microsoft.com/de-de/dotne…?view=dotnet-plat-ext-6.0
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!