Hoe kan ik een "Java Frame" GC Root-verwijzing naar een Runnable verwijderen wanneer ik een heap in jvisualvm dump?

Ik gebruik jvisualvm om te controleren op geheugenlekken in mijn toepassing. Wanneer ik een heap-dump doe, worden soms meerdere objecten opengehouden, zodat er garbage verzameld zou zijn.

When I do a "Show Nearest GC Root" command on them, it shows me that the root is a class which I defined which implements the interface Runnable. The reference is listed as (java frame), which I know has something to do with threading. When I expand the tree for this node, it opens up and shows . So it seems pretty clear that this is not a reference I'm keeping open, but something internal to Java.

Het GC-hoofdobject dat in jvisualvm wordt vermeld, is van het type AnalyticNode breidt knooppunt uit, die op zijn beurt knooppunt implementeert uitvoerbaar is. Dit root-object heeft op geen enkele manier iets te maken met AWT, Swing of andere zwaargewicht componenten van de gebruikersinterface, ondanks het feit dat het woord "frame" wordt gebruikt. In dit geval verwijst het woord "frame" naar draadsnijden.

Dus houdt Java een verwijzing naar de laatste Runnable ergens die dit open zou houden? Is er een manier om Java te laten weten dat deze referentie kan worden vrijgegeven, zodat het goed kan worden verzameld voor mijn heap dump?

Wat is hier aan de hand?

3
Kunt u in het venster "Exemplaren" het exemplaar selecteren dat volgens u zou moeten zijn verzameld, rechtsklikken en "Weergeven in discussies" selecteren in het contextmenu? Zo ja, wat zegt dat?
toegevoegd de auteur erickson, de bron

2 antwoord

In deze context verwijst "frame" naar een stapelframe. Het klinkt als deze Runnable , in plaats van (of als aanvulling op) dat het doelwit is van een lopende thread, wordt deze opgeslagen in een lokale variabele in een frame op de stapel van een uitvoerende thread. Het komt in aanmerking voor verzameling wanneer de methode die aan het frame is gekoppeld, terugkeert.


Op basis van latere opmerkingen is mijn schatting dat in uw aangepaste threadpool een lokale variabele is toegewezen waaraan de Runnable is toegewezen. Het bevindt zich waarschijnlijk in een te groot bereik (buiten een lus) en wordt niet gewist (toegewezen null ) na elke iteratie van de lus.

Ik kan een situatie reproduceren die overeenkomt met wat wordt beschreven met code zoals deze in een werkthread:

Runnable target = null;
while (true) {
  target = queue.take();
  target.run();
}

Het opruimen van de declaratie van doel zodat het binnen de lus zit, repareert het probleem.

Ik zou willen voorstellen om over te schakelen naar een implementatie van Executor vanuit core Java, of de relevante code van uw aangepaste threadpool te plaatsen als u deze wilt repareren.

4
toegevoegd
@ErickRobertson Gebruikt u een Executor van JSE? Welke implementatieklasse?
toegevoegd de auteur erickson, de bron
Als dit werd opgeslagen in een lokale variabele in een Java-kernklasse, zou dat dan niet verschijnen in de jossualvm heap dump? Elk ander object in het systeem heeft een volledige lijst van alle referenties in de heap. Dus ik kan alleen maar aannemen dat elke verwijzing naar dit object van ergens buiten de heap komt.
toegevoegd de auteur Erick Robertson, de bron
Bingo! Het was niet helemaal hetzelfde patroon, maar dit is precies wat er gaande was. De referentie bevond zich in de threadstack in een lokale variabele die nog steeds werd vastgehouden toen de thread wachtte op nog een Runnable . Bedankt voor het geweldige antwoord!
toegevoegd de auteur Erick Robertson, de bron
Trouwens, ik ga ook kijken naar de Executor -service. Toen ik er voor het eerst naar keek, was ik er helemaal door overweldigd en had ik de meeste functies niet nodig. Nu ik ernaar kijk, lijkt het vrij eenvoudig. Het lijkt slechts een paar regels code om het te verwisselen en te testen, dus het staat op mijn lijst met achtergrondprojecten. Het is eenvoudig genoeg om beide manieren te benchmarken en de beter presterende oplossing te bepalen. Nogmaals bedankt voor uw hulp en uw lessen.
toegevoegd de auteur Erick Robertson, de bron

Wat heb je gedaan met het object dat je hebt gemaakt? Heb je een thread gemaakt en ernaar verwezen? In dat geval moet u ervoor zorgen dat de thread is gestopt door de code in run() te laten eindigen.

1
toegevoegd
Het object wordt gegeven aan een thread-pool wanneer het werk heeft dat moet worden gedaan. In de staat waarin ik thread had gedumpt, is het object volledig niet-verwezen en is de threadpool inactief. Er zijn geen overblijvende verwijzingen in code naar dit object. Zou de laatste thread die het draaide een interne verwijzing bevatten naar de laatst gebruikte thread?
toegevoegd de auteur Erick Robertson, de bron
Dat is zeker mogelijk. Natuurlijk zou het niet moeten. Als een experiment, wat als je een Wrapper Runnable maakt die je eenvoudig delegeert naar je echte Runnable en je de referentie handmatig verwijdert in de wrapper die bruikbaar is voor je echte werkruimte? Is uw uitvoerbare schijf geschikt voor GC en blijft de verpakking achter?
toegevoegd de auteur Sarel Botha, de bron
Wat gebeurt er als u de uitvoerder stopt en wat gebeurt er als u een andere uitvoerbare uitvoert? Komt deze vrij?
toegevoegd de auteur Sarel Botha, de bron