RAII und Java

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Chromanoid hat geschrieben:Es ist klar, dass ihr nicht gerne Java programmiert, wenn ihr die Art des Programmierens für unzumutbar haltet. Man kann und sollte Java nicht wie C++ programmieren. Wer das versucht, der kann sich nicht auf andere Sprachen einlassen. Es gibt nicht wenige Sprachen bei denen es wie in Java zugeht und die kommen auch alle ohne RAII aus.
Ich würde eher sagen dass man in diesen Sprachen zwangsweise irgendwie ohne RAII auskommen muss. Es geht nicht darum, Java wie C++ zu programmieren, sondern darum dass gewisse Dinge in Java oder z.B. auch C# einfach rein prinzipiell nicht wirklich vernünftig lösbar sind. Durch den Garbage Collector werden zwar die Probleme mit dem Speichermanagement verschleiert, der Preis den man dafür bezahlt ist aber, dass nun sämtliche anderen Arten von Ressourcen zum Alptraum werden. RAII ist eben die beste Lösung die ich kenne. Ich hätte nichts dagegen, wenn Java, C# oder sonst eine Sprache eine brauchbare Alternative bieten würden. Das Problem ist aber, dass eben genau das nicht der Fall ist.
Chromanoid hat geschrieben:Mit meinen Beispielen wollte ich versuchen Probleme zu konstruieren, bei denen RAII im Weg steht. Jedes Konzept hat seine Nachteile und die habe ich versucht herauszukitzeln. Wenn meine Beispiele nicht schmecken, dann würde ich darum bitten selbst welche zu konstruieren. Nicht damit ich sagen kann warum ich Java toller finde, sondern einfach um den Sachverhalt besser zu verstehen. Meine Vermutung ist immer noch, dass RAII in Punkto Erweiterbarkeit Probleme machen kann.
Das war mir schon klar. Aber da bist du in der falschen Richtung unterwegs, denk ich. Mir fällt nur leider auch nicht wirklich was ein. Ein "Problem" von RAII wäre, dass man sich eben wirklich ernsthaft mit den Abhängigkeiten in seinem Programm befassen muss und nicht einfach wild drauf losprogrammieren kann. Wenn man das nicht tut, wird man irgendwann sehr unsanft daran erinnert. Das ist jetzt aber eben auch nicht unbedingt ein Nachteil, immerhin führt das am Ende natürlich wieder zu besserem Design...
Zuletzt geändert von dot am 25.05.2012, 11:27, insgesamt 1-mal geändert.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Das heißt RAII ist aus eurer Sicht alternativlos und die Lösung aller Probleme, die beim Einsatz von Ressourcen auftreten?
Ist es nicht so, dass alles was man mit RAII-konformen Objekten macht, auf dem Stack passieren muss (und folglich die Größe der Objekte im Vorhinein bekannt sein muss?) oder man auf Referenzzählung angewiesen ist? Dadurch unterliegt man ja erst mal keinen Einschränkungen, aber viele Probleme werden sicher nicht mehr so einfach überblickbar sein. Und an diesem Punkt ist abzuwägen, ob man lieber mit GC programmiert oder auf RAII setzt?!
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Chromanoid hat geschrieben:Das heißt RAII ist aus eurer Sicht alternativlos und die Lösung aller Probleme, die beim Einsatz von Ressourcen auftreten?
Nur weil mir keine ähnlich mächtige Alternative bekannt ist, heißt das noch lange nicht, dass ich es als alternativlos betrachte. ;)
Chromanoid hat geschrieben:Ist es nicht so, dass alles was man mit RAII-konformen Objekten macht, auf dem Stack passieren muss (und folglich die Größe der Objekte im Vorhinein bekannt sein muss?) oder man auf Referenzzählung angewiesen ist? Dadurch unterliegt man ja erst mal keinen Einschränkungen, aber viele Probleme werden sicher nicht mehr so einfach überblickbar sein. Und an diesem Punkt ist abzuwägen, ob man lieber mit GC programmiert oder auf RAII setzt?!
Ich glaub da liegt ein Missverständnis vor. Die RAII Objekte selbst müssen "auf dem Stack" liegen. Die von ihnen verwalteten Objekte können aber irgendwas sein. Referenzzählung hat mit all dem erstmal überhaupt nichts zu tun. Referenzzählung ist einfach nur eine Lösung um shared Ownership zu implementieren. RAII existiert völlig unabhängig davon, ist aber natürlich auch darauf wunderbar anwendbar.
RAII zwingt einen naturgemäß dazu, sich mit den Abhängigkeiten in seinem Programm zu befassen. Wenn man das nicht tut, kann man sich in der Tat recht komplexe Probleme erschaffen. Allerdings sind die dann wieder nur Symptome vom erzeugenden, schlechten Design. Ein Garbage Collector behandelt diese Symptome, nicht aber die Ursachen. RAII dagegen zwingt einen dazu, sich um die Ursachen zu kümmern, da das Ganze sonst einfach gar nicht erst kompiliert oder einem alles um die Ohren fliegt...
Zuletzt geändert von dot am 25.05.2012, 11:45, insgesamt 11-mal geändert.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Nachtrag: Dieser Post dupliziert vieles, was dot gesagt hat. Aber Wiederholung kann an dieser Stelle eigentlich gar nicht schaden. ;)
Chromanoid hat geschrieben:Das heißt RAII ist aus eurer Sicht alternativlos und die Lösung aller Probleme, die beim Einsatz von Ressourcen auftreten?
Nein, nicht alternativlos, aber die einzige funktionierende Lösung, die wir kennen.
Chromanoid hat geschrieben:Ist es nicht so, dass alles was man mit RAII-konformen Objekten macht, auf dem Stack passieren muss (und folglich die Größe der Objekte im Vorhinein bekannt sein muss?) oder man auf Referenzzählung angewiesen ist?
Nein, in seinem vollständigen Umfang ist das Konzept auch temporär anwendbar, der Ort der verwalteten Ressource ist unerheblich. Ein verwaltendes Objekt kann dabei Verwaltung übernehmen oder abgeben, und nur das verwaltende Objekt ist an den Geltungsbereich gebunden.
Chromanoid hat geschrieben:Dadurch unterliegt man ja erst mal keinen Einschränkungen, aber viele Probleme werden sicher nicht mehr so einfach überblickbar sein. Und an diesem Punkt ist abzuwägen, ob man lieber mit GC programmiert oder auf RAII setzt?!
Es ist die Frage, ob diese Probleme denn dann mit GC je überblickbar waren. Versteckte Abhängigkeiten werden softwaretechnisch eigentlich immer verurteilt, sogar von Java-Programmierern. ;)

Aber ich wiederhole es einfach nochmal: Selbst mit Unterstützung von derlei versteckten Abhängigkeiten könnten GC und RAII orthogonal nebeneinander existieren.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

CodingCat hat geschrieben:Aber ich wiederhole es einfach nochmal: Selbst mit Unterstützung von derlei versteckten Abhängigkeiten könnten GC und RAII orthogonal nebeneinander existieren.
Ja, man braucht eben Objekte, die an einen Scope gebunden sind. Das ist allerdings schon eine ziemlich komplexe Unterscheidung.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Es gibt Sprachen, die beides bieten ;)
Machbar ist es also und wird auch gemacht. Auch wenn ich persönlich es für keine besonders gute Idee halte. Aber es gab eben mal diese Zeit, da musste alles einen Garbage Collector haben um als Sprache ernstgenommen zu werden. Zum Glück geht diese Ära langsam wieder zu Ende...
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Wo wir uns schon so viel damit befasst haben, werde ich auch mal etwas konstruktiv: Hässlich, aber nützlich.

Code: Alles auswählen

public ScopeGuard<Guarded implements AutoCloseable> implements AutoCloseable {
   private Guarded v;
   
   public ScopeGuard() {
      this.v = null;
   }

   public ScopeGuard(Guarded v) {
      this.v = v;
   }
   
   public void close() throws Exception {
      if (this.v != null)
         this.v.close();
   }
   
   public Guarded get() {
      return this.v;
   }
   
   public Guarded detach() {
      Guarded d = this.v;
      this.v = null;
      return d;
   }
}

// Usage:

SomeResource resourceOnSuccess;

try ( ScopeGuard<SomeResource> myResource = new ScopeGuard<SomeResource>(new SomeResource(...)) ) {
   // ...
   myResource.get().doStuff(...);
   // do more dangerous stuff ...
   resourceOnSuccess = myResource.detach();
   // also possible: return myResource.detach();
}

// use resourceOnSuccess
Man beachte, dass das nicht 100% ausnahmesicher ist, sollte ScopeGuard bei seiner Erzeugung eine OutOfMemoryException hervorrufen. Darunter leiden aber wohl sehr sehr viele try-with-resources-Beispiele. Wer braucht schon lückenlos? ;)
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Mmh, da verstehe ich nicht so genau was die Schachtelung nützt. Entweder die Ressource SomeResource wurde korrekt initialisiert oder nicht. Da muss man doch nicht noch was drum herum bauen. Wenn man SomeResource bereits als Instanz wo anders her nimmt, wäre das einfach eine verkomplizierte Variante das ganze zu schließen. try ist doch in diesem Sinne schon der Scope-Guard.
edit: Verstehe nun was du meinst. Eine Ressource nutzen ohne ein try drum zu bauen und das ganze schließen zu müssen.

Wenn man mehrere Ressourcen in einer Oberressource verwaltet, kann ich mir eher sowas als nützlich vorstellen:

Code: Alles auswählen

public static void closeAll(AutoCloseable... closables) throws Exception{
    Exception exception=null;
    for(AutoCloseable closable:closables){
        try{
            closable.close();
        } catch(Exception ex) {
            if(exception==null){
                exception=ex;
            } else {
                exception.addSuppressed(ex);
            }             
        }
    }
    if(exception!=null)
         throw exception;
}
//usage
//in parent resource
ParentResource(){
 x=new FileInputStream(...);
 y=new FileInputStream(...);
}
close(){
 closeAll(null,x,y);
}
bzgl. out of memory: soweit ich das verstehe, sollten die ressourcen geschlossen werden, solange es technisch noch möglich ist. das müsste in c++ genauso problematisch sein.
Zuletzt geändert von Chromanoid am 25.05.2012, 13:18, insgesamt 3-mal geändert.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Chromanoid hat geschrieben:Mmh, da verstehe ich nicht so genau was die Schachtelung nützt. Entweder die Ressource SomeResource wurde korrekt initialisiert oder nicht.
Genau, und wenn nicht, dann soll sie bitte auch nicht leaken.
Chromanoid hat geschrieben:Da muss man doch nicht noch was drum herum bauen.
Doch:

Code: Alles auswählen

class HeavyOnResources implements AutoCloseable {
   private ResourceA resA;
   private ResourceB resB; // optional, beliebig viele weitere
   // ...

   public HeavyOnResources(...) {
      try (
         ScopeGuard<ResourceA> resA = new ScopeGuard<ResourceA>(new ResourceA(...));
         ScopeGuard<ResourceB> resB = new ScopeGuard<ResourceB>(new ResourceB(...));
         // ...
      ) {
         this.resA = resA.detach();
         this.resB = resB.detach();
      }
   }

   public void close() throws Exception {
      try (
         ScopeGuard<ResourceA> closeA = new ScopeGuard<ResourceA>(this.resA);
         ScopeGuard<ResourceB> closeB = new ScopeGuard<ResourceB>(this.resB);
         // ...
      ) { }
   }
}
Tada, ein (fast) ausnahmesicherer Konstruktor und Destruktor in Java. Versuch das mal ohne ScopeGuard zu formulieren, das wird auf jeden Fall redundant mit manuellem close() und in der kürzesten Form vmtl. mit ekelhaftem if (... != null).
Chromanoid hat geschrieben:Wenn man mehrere Ressourcen in einer Oberressource verwaltet, kann ich mir eher sowas als nützlich vorstellen:

Code: Alles auswählen

public static Exception closeAll(Exception exception, AutoCloseable... closables){
    for(AutoCloseable closable:closables){
        try{
            closable.close();
        } catch(Exception ex) {
            if(exception==null){
                exception=ex;
            } else {
                exception.addSuppressed(ex);
            }             
        }
    }
    return exception;
}
Pass auf, gleich packe ich doch noch Reflection aus. ;)
Zuletzt geändert von CodingCat am 25.05.2012, 12:44, insgesamt 1-mal geändert.
Grund: Fehlendes Interface AutoCloseable ergänzt.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

habs ja jetzt verstanden... dein obiges beispiel ist etwas problematisch, wenn resA.close() scheitert wird resB.close() nicht mehr aufgerufen. (edit: hast du ja verbessert)
Zuletzt geändert von Chromanoid am 25.05.2012, 12:38, insgesamt 1-mal geändert.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Ja, mit deiner ekligen Schleife hast du mich tasächlich noch auf einen weiteren Anwendungsfall beim Schließen gebracht, denn tatsächlich ist sogar die manuelle close()-Methode mit mehreren Ressourcen ohne Hilfsmittel alles andere als trivial. Auch hierfür lässt sich der ScopeGuard sehr elegant einsetzen, siehe korrigiertes Codebeispiel im Post obendrüber.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

hehe huch schon wieder editiert als du geschrieben hast.... dein ScopeGuard oder meine closeAll-Funktion könnte man jetzt noch per Annotation o.Ä. automatisch einbauen. Wenn man eine Resource zeitlich versetzt unabhängig verarbeitet, ist der ScopeGuard auch ziemlich nett. Allerdings kommt man damit leider immer noch nicht ohne Behandlung, ob das ganze schon geschlossen wurde, aus.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Ich denk damit ist nun aber sehr schön demonstriert, was für ein mächtiges Werkzeug RAII doch ist. Denn RAII bietet eine unglaublich simple und konsistente Lösung mit der sich all diese Fragen schlagartig gar nicht mehr stellen...
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: RAII und Java

Beitrag von Chromanoid »

Ja, RAII ist da tatsächlich sehr angenehm. Allerdings fügt man einer Sprache zur Unterstützung auch eine nicht zu unterschätzende Komplexität hinzu, da man ja Möglichkeiten vorsehen muss Objekte auf einen bestimmten Scope zu beschränken, den Scope zu wechseln etc. und als Entwickler dann entscheiden muss inwiefern man diese Möglichkeiten einsetzen will. try-with-resources ist da ja noch ziemlich harmlos, wenn man sich überlegt, welche Möglichkeiten in C++ für echtes RAII bestehen.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: RAII und Java

Beitrag von dot »

Chromanoid hat geschrieben:Allerdings fügt man einer Sprache zur Unterstützung auch eine nicht zu unterschätzende Komplexität hinzu [...]
Konstruktoren und Destruktoren fügen imo jetzt nicht gerade dermaßen viel Komplexität hinzu. Vor allem: Im Vergleich zu welcher Alternative?
Chromanoid hat geschrieben:[...] da man ja Möglichkeiten vorsehen muss Objekte auf einen bestimmten Scope zu beschränken, den Scope zu wechseln etc. und als Entwickler dann entscheiden muss inwiefern man diese Möglichkeiten einsetzen will. try-with-resources ist da ja noch ziemlich harmlos, wenn man sich überlegt, welche Möglichkeiten in C++ für echtes RAII bestehen.
Man braucht da nicht irgendwelche dermaßen besonderen Möglichkeiten. Scope ist etwas völlig natürliches, gibts in Java ja auch, nur ist die Lebensdauer von Objekten dort eben außer bei built in Typen nicht an den Scope gebunden, bzw. es gibt keinen Weg eine solche Bindung herzustellen. Den Scope "wechseln" macht wie gesagt keinen Sinn. Aber man kann die Ownership über ein Objekt übertragen etc. Das läuft aber alles nichtmehr über irgendwelche Sprachmittel sondern ist bereits Teil der jeweiligen RAII Implementierung.
Scope und an den Scope geknüpfte Lebensdauer von Objekten ist in der Tat alles was eine Sprache braucht. Das Fehlen von letzterem ist genau das, woran es in Java scheitert.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: RAII und Java

Beitrag von CodingCat »

Chromanoid hat geschrieben:try-with-resources ist da ja noch ziemlich harmlos, wenn man sich überlegt, welche Möglichkeiten in C++ für echtes RAII bestehen.
Nein, in Bezug auf Komplexität ist RAII in C++ nicht viel mehr als Javas try-with-resources, automatisch und konsequent an den richtigen Stellen eingefügt. (Siehe mein ScopeGuard-Java-Beispiel, mit dem sich bereits etwa die Hälfte der genannten Probleme durch eine einzige triviale Klasse lösen lassen, wenn auch in der Anwendung hässlich und umständlich.)

Der Unterschied ist, in C++ entstand RAII völlig natürlich aus der konsequenten Durchdenkung und Umsetzung von Objektkonstruktion und Destruktion, während Java derlei Mechanismen nun träge und lückenhaft kopieren muss, weil das Konzept des Garbage Collectors logische und physikalische Objektkonstruktion von Anfang an durcheinander gebracht hat. Und damit haben wir endlich den Bogen zum Ausgangszitat geschafft.
Ein Garbage Collector ist ein Designfehler.
;)

Das Erschreckende ist, dass sich die Wenigsten dieser Problematik bewusst sind. Für die meisten Programmierer scheint ein Objekt noch immer gleichwertig mit einem Stück Speicher zu sein. Und solange das so bleibt, werden wir weiterhin zur einen Hälfte Ausnahmen verteufeln und zur anderen Hälfte unsere Programme damit in den vielfältigsten Weisen zerstören, von subtilen Verhaltensfehlern über massiven Datenverlust bis hin zu gewaltigen Crashes.
Zuletzt geändert von CodingCat am 25.05.2012, 15:02, insgesamt 2-mal geändert.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: RAII und Java

Beitrag von Aramis »

Ein Garbage Collector ist ein Designfehler.
Das wuerde ich sofort so unterschreiben.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: RAII und Java

Beitrag von Chromanoid »

Würde man RAII in Java einbauen müsste man ja alles, was mit den jew. Ressourcen zu tun hat, ebenfalls an einen Scope binden, da sonst mögliche Referenzen erst zu einem unbestimmten Zeitpunkt verschwinden würden. Man würde also erst mal auf einen GC verzichten und nur noch mit RAII arbeiten. Nun hätte man wiederum bei der Vorgabe, Reflexion und dynamische Erweiterbarkeit zu garantieren, viel damit zu tun Referenzen zwischen Objekten auf dem Heap zu überwachen. Und dann fängt man doch wieder an, sich über einen GC Gedanken zu machen... Ich sehe im GC keinen Designfehler, man erkauft sich mit dem Verzicht auf RAII und dem Einsatz des GC eben verschiedene Vorteile, die vor allem mit Relfexion und Behandlung unbekannter Objekttypen zu tun haben.

Welche Sprachen bieten denn eigentlich GC+RAII an? In Kombination sollte das doch ziemlich unnütz sein.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: RAII und Java

Beitrag von CodingCat »

Chromanoid hat geschrieben:Würde man RAII in Java einbauen müsste man ja alles, was mit den jew. Ressourcen zu tun hat, ebenfalls an einen Scope binden, da sonst mögliche Referenzen erst zu einem unbestimmten Zeitpunkt verschwinden würden. Man würde also erst mal auf einen GC verzichten und nur noch mit RAII arbeiten.
Das ist wohl die wichtigste Erkenntnis dieses Threads, genau deshalb braucht eigentlich niemand einen Garbage Collector.
Chromanoid hat geschrieben:Nun hätte man wiederum bei der Vorgabe, Reflexion und dynamische Erweiterbarkeit zu garantieren, viel damit zu tun Referenzen zwischen Objekten auf dem Heap zu überwachen.
Weil ich das erst nachträglich oben rein editiert hatte, hier gleich nochmal: Für die meisten Programmierer scheint ein Objekt noch immer gleichwertig mit einem Stück Speicher zu sein. Und solange das so bleibt, werden wir weiterhin zur einen Hälfte Ausnahmen verteufeln und zur anderen Hälfte unsere Programme damit in den vielfältigsten Weisen zerstören, von subtilen Verhaltensfehlern über Ressourcenlecks bis hin zu massivem Datenverlust.
Chromanoid hat geschrieben:Und dann fängt man doch wieder an, sich über einen GC Gedanken zu machen... Ich sehe im GC keinen Designfehler, man erkauft sich mit dem Verzicht auf RAII und dem Einsatz des GC eben verschiedene Vorteile, die vor allem mit Relfexion und Behandlung unbekannter Objekttypen zu tun haben.
Reflection und unbekannte Objekttypen kommen mit wohldefinierten (geteilten) Besitzabhängigkeiten wunderbar ohne GC aus. Das einzige, was ein guter Garbage Collector wirklich gut kann, ist, verwaiste Objekte aufzuspüren. Der Preis dafür ist, dass der gesamte Programmzustand aufs Spiel gesetzt wird. Ein Preis, der mir in praktisch keinem Kontext angemessen erscheint.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: RAII und Java

Beitrag von Chromanoid »

CodingCat hat geschrieben:
Chromanoid hat geschrieben:Nun hätte man wiederum bei der Vorgabe, Reflexion und dynamische Erweiterbarkeit zu garantieren, viel damit zu tun Referenzen zwischen Objekten auf dem Heap zu überwachen.
Weil ich das erst nachträglich oben rein editiert hatte, hier gleich nochmal: Für die meisten Programmierer scheint ein Objekt noch immer gleichwertig mit einem Stück Speicher zu sein. Und solange das so bleibt, werden wir weiterhin zur einen Hälfte Ausnahmen verteufeln und zur anderen Hälfte unsere Programme damit in den vielfältigsten Weisen zerstören, von subtilen Verhaltensfehlern über massiven Datenverlust bis hin zu gewaltigen Crashes.
Ich denke genau bei diesem und deinem anderen Punkt scheiden sich die Geister. Die meisten, die mit einer GC Sprache programmieren, geben den Bezug von Objekten zu Speicher einfach auf. Es gibt einfach ein Objekt, das verschiedene Eigenschaften und Methoden beinhaltet. Der Rest ist irrelevant. Ich sehe es so, dass man bei den meisten Sprachen, die einen GC einsetzen, ziemlichen Mist programmieren kann, ohne dass dieser Mist zu schwereren Fehlern führt und dass man auch komplexe Besitzabhängigkeiten ohne groß nachzudenken bewältigen kann.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: RAII und Java

Beitrag von CodingCat »

Chromanoid hat geschrieben:Die meisten, die mit einer GC Sprache programmieren, geben den Bezug von Objekten zu Speicher einfach auf.
Das meinen sie vielleicht. Tatsächlich setzt der GC implizit Objekte und Speicherstücke gleich.
Chromanoid hat geschrieben:Ich sehe es so, dass man bei den meisten Sprachen, die einen GC einsetzen, ziemlichen Mist programmieren kann, ohne dass dieser Mist zu schwereren Fehlern führt und dass man auch komplexe Besitzabhängigkeiten ohne groß nachzudenken bewältigen kann.
Ja, das ist wohl unbestritten. Aber spielt die Schwere der Fehler wirklich eine Rolle? Ist es nicht gruselig, wenn ein Programm nach und nach in einen immer inkonsistenteren Zustand trudelt?
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: RAII und Java

Beitrag von Chromanoid »

CodingCat hat geschrieben:
Chromanoid hat geschrieben:Die meisten, die mit einer GC Sprache programmieren, geben den Bezug von Objekten zu Speicher einfach auf.
Das meinen sie vielleicht. Tatsächlich setzt der GC implizit Objekte und Speicherstücke gleich.
Genau damit hat man aber als Entwickler dann nichts mehr zu tun. Und dass man eine Datei oder Ähnliches nach dem "Öffnen" wieder "Schließen" muss, ist wesentlich vermittelbarer als RAII.
CodingCat hat geschrieben:
Chromanoid hat geschrieben:Ich sehe es so, dass man bei den meisten Sprachen, die einen GC einsetzen, ziemlichen Mist programmieren kann, ohne dass dieser Mist zu schwereren Fehlern führt und dass man auch komplexe Besitzabhängigkeiten ohne groß nachzudenken bewältigen kann.
Ja, das ist wohl unbestritten. Aber spielt die Schwere der Fehler wirklich eine Rolle? Ist es nicht gruselig, wenn ein Programm nach und nach in einen immer inkonsistenteren Zustand trudelt?
Die Schwere der Fehler spielt eine ziemlich bedeutende Rolle. Dass es immer "inkonsistenter" wird, sehe ich nicht so, einzelne Komponenten arbeiten vielleicht falsch, aber die kann man zur Not on-the-fly verbessern und austauschen. Das einzige Problem sind doch wohl externe oder geteilte Ressourcen, die nicht verfügbar sind bzw. nicht freigegeben werden. Das Problem kann man mit RAII auch nur bedingt umgehen. RAII spielt bei C++ eine wesentlich wichtigere Rolle, da die Alternativen normale Objekte zu erzeugen so "schrecklich" sind, wie in Java externe Ressourcen (wie Dateien und Verbindungen) verwaltet werden müssen.
NytroX
Establishment
Beiträge: 367
Registriert: 03.10.2003, 12:47

Re: RAII und Java

Beitrag von NytroX »

Hi,

meiner Meinung nach hat ein try-with-resources in Java ein (nicht) ganz einfaches Problem: den GC.
Und zwar ist afaik RAII nicht orthogonal zum GarbageCollector, weil der GC niemals die Reihenfolge bzw. den Zeitpunkt beim Zerstören.. ääh.. "Finalisieren" der Objekte vorgibt (zumindest in Java und C#, im Gegensatz zu RAII in C/C++).

Das führt zwangsweise zu Problemen, die man nur umgehen kann wenn:
- man trotz GC eine Möglichkeit hat, Objekte explizit feizugeben (wie z.B. in D)
- man GC und RAII immer komplett trennt (wie z.B. in ManagedC++)


Aber speziell das try-with-resources hat noch ein ganz anderes Problem: man muss genau wissen was man tut.

Z.B:

Code: Alles auswählen

try (BufferedReader br = new MyBufferedReader(  
            new MyFileReader("C:/temp/file.txt"))) {  
            System.out.println(br.readLine());  
        }  
kann ein Resource-Leak sein. Und meiner Meinung nach ist das alles andere als offensichtlich.


Ich erlebe in der Praxis öfter Probleme, weil sich Java-Programmierer darauf verlassen, dass der GC sowas schon schaukelt. Tut er aber meistens nicht, jedenfalls nicht wenn das Programm mal 3 Tage am Stück performant rennen soll.


Aber inzwischen frage ich mich, ob man überhaupt einen GC braucht.
Denn: Man hat im Alltag eigentlich immer 2 Typen von Daten bzw Objekten. Und das sind:

a)
Stammdaten. Diese brauche ich länger. Das wären z.B. Listen, Daten in der Datenbank, ein Scenegraph, usw.
An diesen Daten ist besonders, dass die Runtime NIE wissen kann, wie lange ich sie brauche. Ein GC würde sie ewig aufheben, RAII/ReferenceCount hilft nicht viel, da ich sie länger brauche als den aktuellen Scope.
Und spätestens wenn ich sie in eine Datenbank schreibe (wie auch immer die geartet ist), muss ich die Datensätze auch selbst wieder löschen.

b)
temporäre Daten. Dies sind die Daten, mit denen ich aktiv arbeite. Sie sind nur kurz da, oder sind (in irgendeiner Form) Referenzen zu den Stammdaten.
Hier weiss ich, dass ich sie wegräumen kann, wenn ich mit der Verarbeitung fertig bin (z.B. nach einem MainLoop-Durchlauf). Bei einem GC riskiere ich, dass die Daten zu lang im Speicher liegen oder ich an den Parametern des GC rumbasteln muss damit das Ganze irgendwie performant ist. Bei anderen Systemen riskiere ich, dass der Speicher fragmentiert oder unnötig Zeit beim Instanzieren verloren geht, der GC könnte da schön einen ganzen Speicherblock gleichzeitig freigeben.

Fazit: Sowohl RAII, ReferenceCount als auch der GC sind Konstrukte, mit denen ich mich eigentlich garnicht rumärgern will, und es eigentlich auch nicht müsste.

Achja: und wohin gehören nun Ressourcen wie Dateien oder Socket-Verbindungen ?
Antwort:
teils-teils. Man hat einen ResourceManager (der gehört zu a) und eine konkrete Instanz, die man vor der Nutzung vom Manager anfragt und danach wieder abgibt (also b).
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4262
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: RAII und Java

Beitrag von Chromanoid »

NytroX hat geschrieben:Aber speziell das try-with-resources hat noch ein ganz anderes Problem: man muss genau wissen was man tut. Z.B:

Code: Alles auswählen

try (BufferedReader br = new MyBufferedReader(  
            new MyFileReader("C:/temp/file.txt"))) {  
            System.out.println(br.readLine());  
        } 
kann ein Resource-Leak sein. Und meiner Meinung nach ist das alles andere als offensichtlich.
Stimmt natürlich, try-with-resources lädt da zu solchen gefährlichen Spielchen ein. Nur fürs Protokoll, "richtig" wäre:

Code: Alles auswählen

try (MyFileReader mfr=new MyFileReader("C:/temp/file.txt"); BufferedReader br = new MyBufferedReader(mfr)) {  
    System.out.println(br.readLine());  
} 
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: RAII und Java

Beitrag von CodingCat »

NytroX hat geschrieben:Und zwar ist afaik RAII nicht orthogonal zum GarbageCollector, weil der GC niemals die Reihenfolge bzw. den Zeitpunkt beim Zerstören.. ääh.. "Finalisieren" der Objekte vorgibt (zumindest in Java und C#, im Gegensatz zu RAII in C/C++). Das führt zwangsweise zu Problemen, die man nur umgehen kann wenn:
- man trotz GC eine Möglichkeit hat, Objekte explizit feizugeben (wie z.B. in D)
Naja, Javas neue verallgemeinerte close()-Methode tut doch genau das. Wenn du das Objekt danach als zerstört betrachtest, hast du eine explizite Freigabe. Dass das Objekt noch in einer Art Zombiezustand vor sich hin vegetiert, bis der GC es einsammelt, ist nicht schön, aber auch nicht wirklich problematisch. Finalisierung und Freigabe des Speichers sind im Rahmen dieses Mechanismus jedenfalls erstmal irrelevant. Die Reihenfolge wird im Rahmen der try-with-resources-Funktionalität durchaus eingehalten. Der Rest lebt in Java ja eh vollständig in diesem Raum unbestimmter Lebensdauer, so wie es unverwaltete C++-Objekte täten. Aber klar, dass das unschön gegenüber einer strikt geordneten und verwalteten Lebensdauer wie in sauberem und modernem C++ ist, haben wir hier ja schon hinlänglich diskutiert.
NytroX hat geschrieben:- man GC und RAII immer komplett trennt (wie z.B. in ManagedC++)
Hast du gerade Managed C++ gesagt? Falls du C++/CLI meinst, das ist keine Sprache, sondern ein ... Kompatibilitätskonstrukt. ;)
NytroX hat geschrieben:Aber speziell das try-with-resources hat noch ein ganz anderes Problem: man muss genau wissen was man tut.

Code: Alles auswählen

try (BufferedReader br = new MyBufferedReader(  
            new MyFileReader("C:/temp/file.txt"))) {  
            System.out.println(br.readLine());  
        }  
kann ein Resource-Leak sein. Und meiner Meinung nach ist das alles andere als offensichtlich.
Ja, das betrachte ich auch mit großer Sorge, zumal dein Beispiel genau so in dem entsprechenden offiziellen Abschnitt zur Einführung der neuen Sprachfeatures anzutreffen ist.
NytroX hat geschrieben:Ich erlebe in der Praxis öfter Probleme, weil sich Java-Programmierer darauf verlassen, dass der GC sowas schon schaukelt. Tut er aber meistens nicht, jedenfalls nicht wenn das Programm mal 3 Tage am Stück performant rennen soll.
Schön, dass auch Praktiker die Probleme des GCs in Realität sehen. :P
NytroX hat geschrieben:Aber inzwischen frage ich mich, ob man überhaupt einen GC braucht.
Genau, ganz sicher nicht! (Noch ein bisschen Chromanoid ärgern.) ;)
NytroX hat geschrieben:Fazit: Sowohl RAII, ReferenceCount als auch der GC sind Konstrukte, mit denen ich mich eigentlich garnicht rumärgern will, und es eigentlich auch nicht müsste.
Ähm ja klar, C-Style alles zu Fuß geht immer, mit all den damit verbundenen Unsicherheiten. Allerdings dann auf keinen Fall mit Exceptions. Und selbst mit Fehlerbehandlung per Rückgabewerte landet man dann wohl schnell wieder beim goto. Bei RAII geht es doch gerade darum, Ressourcenverwaltung so umzusetzen, dass nichts vergessen werden kann, weil unabhängig von Erfolg oder Fehlschlag immer alles korrekt aufgeräumt und zurückgesetzt wird. Sprich, wenn du dich nicht mit RAII rumärgern willst, dann musst du dich eben mit Ressourcenverwaltung rumärgern.
NytroX hat geschrieben:Achja: und wohin gehören nun Ressourcen wie Dateien oder Socket-Verbindungen ?
Antwort: teils-teils. Man hat einen ResourceManager (der gehört zu a) und eine konkrete Instanz, die man vor der Nutzung vom Manager anfragt und danach wieder abgibt (also b).
Warum auch immer du hier einen Manager brauchst, wenn du eh alles manuell abgibst. Ich bin jedenfalls sehr zufrieden, dass RAII in C++ meinen Code um den Faktor 3 verkürzt, weil sämtliche Fehlerbehandlung und Ressourcenverwaltung automatisiert und wohldefiniert abläuft, ohne dass ich mir darüber je Gedanken machen müsste.

Ich habe in der Vergangenheit leider allzu oft beobachtet, dass Leute, die auf RAII und Ausnahmen zu Gunsten "klassischer" Fehlerbehandlung verzichten, am Ende vor allem auf eins Verzichten, nämlich die Fehlerbehandlung selbst, was ganz sicher nicht richtig ist. ;)
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
NytroX
Establishment
Beiträge: 367
Registriert: 03.10.2003, 12:47

Re: RAII und Java

Beitrag von NytroX »

Hi, Danke für eure Kommentare :-)

@Chromanoid: und fürs Vervollständigen.
CodingCat hat geschrieben:Naja, Javas neue verallgemeinerte close()-Methode tut doch genau das
Ja, das versuchen sie zumindest, aber das ist denke ich die Ursache für die aktuelle Implementierung des try-with-resources, die bei näherer Betrachtung leider eher wie ein Hack aussieht, als ein echtes Sprachkonstrukt... :roll:
CodingCat hat geschrieben:sondern ein ... Kompatibilitätskonstrukt.
Das haste aber nett ausgedrückt :D Achja, heisst ja C++/CLI mittlerweile...
Aber funktioniert verwunderlich gut, um GC und RAII parallel zu nutzen, wenn man mal den ursprünglichen Zweck des Ganzen ignoriert.
CodingCat hat geschrieben: Ähm ja klar, C-Style alles zu Fuß geht immer, mit all den damit verbundenen Unsicherheiten. Allerdings dann auf keinen Fall mit Exceptions. Und selbst mit Fehlerbehandlung per Rückgabewerte landet man dann wohl schnell wieder beim goto. Bei RAII geht es doch gerade darum, Ressourcenverwaltung so umzusetzen, dass nichts vergessen werden kann, weil unabhängig von Erfolg oder Fehlschlag immer alles korrekt aufgeräumt und zurückgesetzt wird. Sprich, wenn du dich nicht mit RAII rumärgern willst, dann musst du dich eben mit Ressourcenverwaltung rumärgern.
Ja, aber ich hätte gerne eine Sprache/Library die mir da, wo es Sinn macht, das RAII schon automatisch bietet. Mit std::lock fangen sie in C/C++ gerade an, was sinnvolles in diese Richtung zu machen, aber die Libs sind da bissel hinterher. Man muss einfach sagen, dass die Standard-Libraries für Java doch sehr umfangreich sind, und für viele Themen gibt es einigermaßen gescheite APIs.



Wenn man sich so mit Java-Produkten und Out-Of-Heap-Space-Errors und NullPointerExceptions rumschlägt (wo es ja in Java garkeine Pointer gibt), hätte man manchmal doch einfach gern die Kontrolle wie bei C/C++.
Aber wenn man nach 2 Wochen in C/C++ einen WebService gebaut hat, der ein UTF-8 XML einigermaßen versteht, mag man doch auch den Komfort von Java, da dauert das Gleiche maximal 1 Tag.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: RAII und Java

Beitrag von CodingCat »

NytroX hat geschrieben:Ja, das versuchen sie zumindest, aber das ist denke ich die Ursache für die aktuelle Implementierung des try-with-resources, die bei näherer Betrachtung leider eher wie ein Hack aussieht, als ein echtes Sprachkonstrukt... :roll:
Auf jeden Fall ist das die Ursache, aber hierzu mache ich jetzt keine Ausführungen mehr, das haben wir ja schon auf den letzten 4 (schon vier?!?) Seiten zur Genüge ausgebreitet. ;)
NytroX hat geschrieben:Aber funktioniert verwunderlich gut, um GC und RAII parallel zu nutzen, wenn man mal den ursprünglichen Zweck des Ganzen ignoriert.
C++/CLI besteht schlicht aus 2 Sprachen in einer. Das muss so sein, weil es eine Brücke zwischen diesen zwei Sprachen schlägt. Beide existieren unabhängig nebeneinander und funktionieren für sich zum Glück auch entsprechend. Aber wozu brauchst du bitte den Managed-Teil, wenn du RAII hast? Es wäre Unsinn, diese kryptische und (bewusst) konzeptlose Sprache für irgendwas anderes als Interop zu missbrauchen.

Dass die Sprache jetzt in der (unmanaged) Variante C++/CX tatsächlich zur Produktivsprache werden soll, ohne dass sich an der kryptischen Syntax oder der Konzeptlosigkeit irgendetwas geändert haben sollte, finde ich btw. grauenvoll. Je nachdem, wie dumm sich MS hier anstellt, könnte C++/CX locker das PHP unter den neuen Anwendungssprachen werden.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: RAII und Java

Beitrag von dot »

CodingCat hat geschrieben:Dass die Sprache jetzt in der (unmanaged) Variante C++/CX tatsächlich zur Produktivsprache werden soll, ohne dass sich an der kryptischen Syntax oder der Konzeptlosigkeit irgendetwas geändert haben sollte, finde ich btw. grauenvoll. Je nachdem, wie dumm sich MS hier anstellt, könnte C++/CX locker das PHP unter den neuen Anwendungssprachen werden.
Da kann ich dich beruhigen. C++/CX hat absolut nichts mit C++/CLI zu tun (auch wenn es auf den ersten Blick ähnlich aussieht) und ist keine eigene Sprache sondern nur ein Satz von C++ Extensions, die den Umgang mit WinRT erleichtern. Es ist wirklich nur normales C++, nur dass der Compiler weiß was ein com_ptr ist und entsprechend optimalen Code dafür generiert (optimaler als man selbst mit move Semantik etc. hinbekommen würde), Syntaxzucker für das Erzeugen von COM kompatiblen Klassen und Instanzieren von COM Komponenten bietet, COM HRESULTS autom. in C++ Exceptions übersetzt und umgekehrt etc. Du kannst theoretisch völlig darauf verzichten und alles selber machen. Dann musst du dich eben händisch mit COM rumschlagen. Es gibt keine CLI, keinen Garbage Collector, keinen managed Heap, keine Interop, nichts. Es ist wirklich nur C++ mit ein paar Extensions die den Umgang mit COM erleichtern. WinRT ist einfach nur COM (sie haben, wohl aus marketingtechnischen Gründen, nur einen neuen Namen draufgeklebt). WinRT Collections sind sogar STL kompatibel. Und zwar wirklich, ohne irgendwelche Compilermagic...

NytroX hat geschrieben:Ja, aber ich hätte gerne eine Sprache/Library die mir da, wo es Sinn macht, das RAII schon automatisch bietet. Mit std::lock fangen sie in C/C++ gerade an, was sinnvolles in diese Richtung zu machen, aber die Libs sind da bissel hinterher.
RAII gehört in C++ doch seit Jahrzehnten zum Standardrepatoire. Zumindest boost bietet schon seit Ewigkeiten ein reichhaltiges Angebot was das angeht...
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: RAII und Java

Beitrag von CodingCat »

dot hat geschrieben:Da kann ich dich beruhigen. C++/CX hat absolut nichts mit C++/CLI zu tun (auch wenn es auf den ersten Blick ähnlich aussieht) und ist keine eigene Sprache sondern nur ein Satz von Spracherweiterungen, die den Umgang mit WinRT erleichtern. [...] WinRT ist einfach nur COM (auch wenn sie's wohl aus marketingtechnischen Gründen nicht so nennen).
Das weiß ich, und genau deshalb bin ich höchst besorgt. Die Anspielung auf PHP bezog sich auf nichts anderes als die Tatsache, dass C++/CX in extremer Anlehnung an seinen Vorgänger C++/CLI managed aussieht, obwohl es das nicht ist. Ich erwarte gigantische Speicher- und Ressourcenlecks, sobald die Leute anfangen, in klassicher C++/CLI-Manier alles mit COM-Referenzen zu überhäufen, in der Annahme, die massiven zirkulären Abhängigkeiten würden wie gewohnt durch einen GC aufgelöst.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: RAII und Java

Beitrag von dot »

Ah, warten wir mal ab. Ich glaub die Leute, die sowas schreiben würden, verwenden sowieso eher C# ;)

Ich bin gespannt wieviele Leute Metro Anwendungen mit C++/CLI schreiben werden :D
Antworten