Hibernate, GarbageCollector und MemoryLeak

Das leidige Thema DBs ???

Folgendes Szenario: Ich habe als Tabellen Äpfel und Bilder. Jeder Apfel besitzt eine Liste von Bildern und jedes Bild genau einen Apfel.
In einer Java Tabelle kann ich mich durchklicken und sehe alle Bilder in einer Übersicht. Problem an der Sache ist, dass irgendwann der Speicher voll läuft. Denn dadurch das ich die Tabelleneinträge anklicke werden die Bilder geladen, damit hängen sie, für mein Verständnis, irgendwo im EntityContext und werden nicht freigegeben bzw. eingesammelt.

Welche Möglichkeiten hat man in so einem Fall das Entitäten “aufgelöst” werden und nicht dauerhaft im Speicher hängen? Bisher bin ich mit der Docu auch nicht wirklich weiter gekommen da sowas wie refresh/merge/reload etc.pp alle nicht das gewünschte Ergebnis haben. Entweder kann man danach nicht mehr persistieren oder es bleibt wie es ist und der Speicher läuft voll.

Schonmal Danke und Grüße

Java Tabelle? Was genau ist deine Java Tabelle? vielleicht musst du da die Daten aufräumen, das der EntityManager diese wieder freigeben kann, da sie nicht benötigt werden. Ich würde annehmen, aktuell fragt deine Tabelle an, gib mir die Entität, gib mit die Entität,… aber sagt nie, diese Entität brauche ich nicht mehr

Ne simple JTable mit meiner Entität als eine Spalte. Wenn ich mich nur durch die Tabelle klicke wird ein “entity.getPics()” aufgerufen. Diese Bilder sind als Liste in dem Entity vorhanden und per “OneToMany”/“ManyToOne” verknüpft. FetchType ist auf lazy.

Wie sag ich denn das ich die Bilder nicht mehr brauche und aufgeräumt werden können? Nach meinem Wissen hab ich alles versucht was ich mir denken könnte. Aber entweder kommt ein persist Fehler beim DB aktualisieren oder es passiert rein gar nichts. Aber Operationen wie merge/reload/update/… führen irgendwie nicht zum Erfolg.

Mal ein Codesnippet wie es grob aussieht

entity = EntityDAO.getByID(10);
List<Pic> picList = entity.getPics(); //hier dann der lazy load
showAll(picList);

//theoretisch hier dann "gib Pic Entitäten wieder frei"

Die Frage ist, ob bei so einer “losen” Kopplung deine Entity wirklich als “Owner” der Pics angesehen werden sollte.

Vielleicht ist die bessere Lösung, zwar beim Erzeugen eines Pics auch ein Entity zu verlangen, aber auf Entity-Seite auf die Relation ganz zu verzichen (also ein unidirektionales @ManyToOne), und das Laden der Liste über eine Query zu erledigen. Damit hast du die Kontrolle über den Lebenszyklus der Pic-Liste, und nicht das Entity.

Natürlich musst du bei dieser Lösung aufpassen, dass du vor dem Löschen von Entities auch “ihre” Pics gekillt werden. Ich weiß nicht, ob du das automatisch hinbekommst oder manuell erledigen müsstest.

Ja das wäre eine Möglichkeit. Aber irgendwie muss es doch auch eine Möglichkeit geben Entitys aus dem EntityManager zu kicken bzw aufzulösen ohne immer den ganzen Manager zu schließen. Nach meinem Laien verständnis ist dafür “merge” da. Um den Zustand eine alten detachten Entitys zu kopieren, auf ein neues attachted.

Naja das Freigeben der Pic Entitäten funktioniert dann nur, wenn du auch deine Entität freigibst, was aber wie es scheint nicht der Fall zu sein scheint und somit denke ich nicht so einfach verwirklichbar ist. Da wäre denke ich der Beste Ansatz der von @Landei vorgeschlagene. Da dann die Entität frei ist und aufgeräumt werden kann. Das ist so nunmal nicht der Fall, da nur ein teil der Entität, das Pic frei wäre, aber nicht deine komplette Entität. Und der GB nicht wissen kann, ob nicht vielleicht später noch der Teil der Entität verwendet wird.

Wenn du ein anderes Objekt hast, das als Member einen String hat, wird der String ja auch nicht gelöscht, nachdem du ihn ein mal Benutzt hast, sondern auch erst, wenn das komplette Objekt aufgeräumt wird.

Hm, ja das macht in der Tat schon Sinn. Die Idee ist also eine getPictures() Methode nicht an das Entity sondern das DAO zu hängen?


public List<Pic> getPictures(long id){
return "select * from Pictures where entityID=id";
}

So grob zumindest?

Ja, das ist die Idee.

Ich vermeide wo immer möglich, nackte IDs herumzureichen (wer garantiert, dass es wirklich eine ID von Entity ist?), würde also die Signatur getPictures(Entity e) empfehlen.

Ja und dadurch sollte nun der GC in der Lage seien, nicht mehr benötigte Bilder auch wieder aufzuräumen, da diese ja dann nicht mehr Bestandteil der Entität sind.

Ob man nun die ID oder die Entität übergibt ist denke ich Geschmackssache. Wenn es die ID nicht gibt, bekommt man halt eine leere Liste, das selbe ist auch der Fall, falls es zu einer Entität kein Bild gibt. Beim übergeben einer Entität, müsste jedoch vor dem Zugriff auf die ID noch ein null-check gemacht werden.

ohne konkrete Anwendung dazu ein Gedanke, der hier glaube ich noch nicht formuliert ist (vielleicht zurecht :wink: ):
nicht bei der noch recht komplizierten und Konsistenz-/ Einstellungen-/ Transaktion-/ Mapping-/ OO-, einfach allgemein-wichtigen Verknüpfung Entity zu Pictures rumspielen,
da alles nach besten Standardmechanismen arbeiten lassen,
→ aber diese Pictures zu leichtgewichtigen Einträgen machen, beliebig in Tabellen und sonstigen allgemeinen Mechanismen zu durchlaufen,
normale wichtige Datenfelder zu bearbeiten, Name, Länge des Bilds als Attribute eingetragen usw., alles ohne das Speicherloch Bild dazu

dafür

  • entweder, wenn möglich, das Bildd als irgendeine Art Lazy-Attribut/ einfaches 1 zu 1-Mapping zu weiterer Tabelle nur für das Bild konfigurieren,
    was unabhängig vom restlichen Inhalt nicht direkt geladen wird wenn in Ruhe gelassen
    (bei allgemeinen Mechanismen wie Serialisierung/ vollständiger Initialisierung immer noch aufpassen)

  • Mapping außen vor lassen, nur Id auf anderen Dateneintrag in der DB, manuell dazu zu laden,
    in etwa wie bisher besprochen, aber für jedes Bild einzeln, einfacher Cache, einfach aufzuräumen, einfach zu prüfen ob Bilder nicht referenziert sind,
    nicht die Verknüpfung Entity zu Pictures

  • DB außen vor lassen, nur Id für andere Ressourcen-Bereiche, etwa nur im Anzeigeschicht, in der Verwaltung Bild unbekannt,
    falls Bild zu Id nicht vorhanden dann etwas was auf logischer Ebene zu bearbeiten ist, ohne gleich eine DB-Konsistenz-Krise zu haben


Mehrfach-Verwendung eines Bildes (z.B. in einem http://forum.byte-welt.net/byte-welt-projekte-projects/fjorum-java-based-forum/ :wink: ) evtl. miteinzubeziehen,
dürfte von Indirektion profitieren, eine Bildressource in mehreren Picture-Einträgen für verschiedene Zwecke,
ob bei Löschen eines Picture-Eintrags das Bild verzichtbar ist, ist dann größere separate allgemeine Aufräumarbeit

Mit mehr Code koennte man konkretere Antworten geben, so bleibt eigentlich nur zu sagen dass der EnitityMananger die Entsprechung einer Session ist, die “Lebenszeit” sollte kurz sein, da alle Entites die nicht explizit enladen wurden vorgehalten (referenziert) werden.

[QUOTE=Landei]Ja, das ist die Idee.

Ich vermeide wo immer möglich, nackte IDs herumzureichen (wer garantiert, dass es wirklich eine ID von Entity ist?), würde also die Signatur getPictures(Entity e) empfehlen.[/QUOTE]

[QUOTE=CyborgGamma;115507]Ja und dadurch sollte nun der GC in der Lage seien, nicht mehr benötigte Bilder auch wieder aufzuräumen, da diese ja dann nicht mehr Bestandteil der Entität sind.

Ob man nun die ID oder die Entität übergibt ist denke ich Geschmackssache. Wenn es die ID nicht gibt, bekommt man halt eine leere Liste, das selbe ist auch der Fall, falls es zu einer Entität kein Bild gibt. Beim übergeben einer Entität, müsste jedoch vor dem Zugriff auf die ID noch ein null-check gemacht werden.[/QUOTE]Gibt es denn eine Art Leitfaden bzw. Best Practice was direkt in das Entity geschrieben und per Annotations verknüpft werden sollte und was man lieber auslagert und das DBMS arbeiten lässt?

[QUOTE=SlaterB;115509]ohne konkrete Anwendung dazu ein Gedanke, der hier glaube ich noch nicht formuliert ist (vielleicht zurecht :wink: ):
nicht bei der noch recht komplizierten und Konsistenz-/ Einstellungen-/ Transaktion-/ Mapping-/ OO-, einfach allgemein-wichtigen Verknüpfung Entity zu Pictures rumspielen,
da alles nach besten Standardmechanismen arbeiten lassen,
→ aber diese Pictures zu leichtgewichtigen Einträgen machen, beliebig in Tabellen und sonstigen allgemeinen Mechanismen zu durchlaufen,
normale wichtige Datenfelder zu bearbeiten, Name, Länge des Bilds als Attribute eingetragen usw., alles ohne das Speicherloch Bild dazu[/QUOTE]Thx für den Post, aber den ersten Teil kann ich nicht ganz nachvollziehen wiel ich nicht verstehe was du mir versuchst zu sagen :frowning: Kannst du es bitte nochmal in einfach vrsuchen?

[QUOTE=maki;115510]Mit mehr Code koennte man konkretere Antworten geben, so bleibt eigentlich nur zu sagen dass der EnitityMananger die Entsprechung einer Session ist, die „Lebenszeit“ sollte kurz sein, da alle Entites die nicht explizit enladen wurden vorgehalten (referenziert) werden.[/QUOTE]Was fehlt dir denn?

@Entity
class TestEntity{
private List<Pic> picList;

@OneToMany(mappedby="entity", fetch=LAZY, orphanremoval=true, cascade=ALL)
@OnDeletion(action=OnDeletionAction.CASCADE)
public List<Pic> getPicList(){
return picList;
}
}

@Entity
class Pic{
private TestEntity entity;

@ManyToOne(fetch=LAZY)
@JOINColumn(name="entityID", nullable=false)
public TestEntity getEntity(){
return entity;
}

Wo steckt denn der EntityManager? :wink:
Initialisierung/Injection, Verwendung, Gueltigkeitsbereich/Lebensdauer, eben das was ich oben angesprochen hatte.

Der hängt in der jeweiligen DAO Klasse bzw einer AbstraktenKlasse die dann alle anderen nutzen.

private EntityManager em = DBUtil.getEntityManager(); //in der Util wird return EntityManagerfactory.createEntityManager(); aufgerufen
}

class TestEntityDAO extends AbstrDAO{
public delete()...
public getAll()...
public persist()...
public macheKrasseSachen()....
}

Lebenszeit und Gültigkeitsdauer ist also solange wie die Anwendung geöffnet ist und für alle Entitäten der gleiche Manager. Was, denke ich, an sich schon nicht so clever ist wenn ich darüber nachdenke.

nicht die wichtige Verknüpfung (mit Cascade und allen modernen Schnickschnack, Hibernate-Kontrolle) Entity-Pictures kaputt machen,
Laden von Pictures wie bisher zulassen, aber Pictures ohne 100 KB-Bilddaten,

diese Bilddaten erst separat kompliziert zu Picture dazuholen

Eben!

Das ist der Grund warum deine Entities im Context rumhaengen und nicht aufgeraeumt werden koennen.

[QUOTE=SlaterB]nicht die wichtige Verknüpfung (mit Cascade und allen modernen Schnickschnack, Hibernate-Kontrolle) Entity-Pictures kaputt machen,
Laden von Pictures wie bisher zulassen, aber Pictures ohne 100 KB-Bilddaten,

diese Bilddaten erst separat kompliziert zu Picture dazuholen[/QUOTE]Wie soll das denn gehen? Wenn ich dem Entity sage “getPictures” dann hab ich aktuell sofort bis zu 0.06MB verbraten. Dazwischen kommt doch nichts mehr, oder etwa doch?

[QUOTE=maki;115518]Eben!

Das ist der Grund warum deine Entities im Context rumhaengen und nicht aufgeraeumt werden koennen.[/QUOTE]
Dann bleibt ja nur noch der Weg direkt beim DAO einen Manager zu öffnen/schließen. Ist das dann aber nicht deutlich aufwendiger/komplizierter als das Teil einmal zu öffnen? Oder ist die eigentliche Vorgehensweise schon genauso und man hunderte Manager auf und zu während einer Anwendungssession?

wow, was für eine Speichermenge :wink: wobei auch gar nicht so wenig, je nachdem wieviel Objekte dafür geladen werden,
also wenn dir jedes geladene Objekt der DB allgemein ein Dorn ist, dann sind natürlich allgemeine Lösungen angebracht,


falls speziell die Bilder ein Problem sind, wie es am Anfang klang

und nach meinem Daumenüberschlag 1 Bild = 100 KB = so viel wie 100 und mehr sonstige Hibernate-Objekte, ein respektables Problem,
Ziel: für 60 Pictures nur 0.06 MB statt 6.06 MB

dann ist eben mein Vorschlag, die Pictures-Objekte weiterhin ganz normal zu laden, genau wie alle anderen Hibernate-Objekte,
aber nur die jeweils 100KB-Bilddaten herauszulassen,
→ an dieser 1 zu 1-Verküpfung, 100 KB für das Picture, arbeiten,

nicht die Pictures zu Entity kompliziert separat laden, wobei sie ja so oder so geladen werden müssen falls ‚in der Tabelle durchgeklickt‘,
Separation nur mit Ziel, sie danach wieder abzuräumen?

besser gar nicht erst die jeweils 100 KB Bilddaten laden, wenn möglich und aktuell eben nicht benötigt, nicht angezeigt werden,
das schont auch die DB,

In jeder Doku zu JPA wird einem erklaert dass der EM kurzlebig sein sollte, das ist der richtige Weg, das andere nicht :wink:

D.h. es ist besser beim Auruf einer DAO Methode einen EM zu holen, ihn danach wieder zu schliessen.

oder alternativ das Konstrukt mit den Basisklassen so belassen, da bin ich ein Fan,
nur: die DAO nicht über ganze Programmlaufzeit belassen, sondern an konkreten Punkten alles komplett neu aufbauen

konkrete Punkte = ein Web-Request, eine Aktion in einer Swing-GUI, nach sleep eines Threads usw.,
bei durchgängig laufenden Programmen schwieriger

Neuaufbau:
inital alles prüfen, Request, Programmzustand, DB-Verbindung, einen Kontext mit Session/ EntityManager, was immer es so gibt, aufbauen,
benötigte DB-Klassen wie TestEntityDAO mit getTestEntityDAO() bei Bedarf holen (neu erstellen) und im Hintergrund mit dem aktuellen EntityManager usw. ausstatten