Java, co się dzieje z anonimowymi obiektami

0

Witam
Napisałem program w którym do listy cały czas są dopisywane nowe anonimowe obiekty i usuwane, zauważyłem że program po 10 minutach zaczyna ścinać. Czy to oznacza że te obiekty cały czas są przetrzymywane w pamięci, jeśli tak jest to jak się ich pozbywać ?

3

Nie znam pojęcia anonimowe obiekty ale GC powinien, po pewnym czasie, usunąć wszystkie obiekty na które nie ma żadnej (zwykłej/mocnej) referencji.
Bez kodu i opcji uruchomienia trudno wróżyć. Możesz też użyć profilera żeby zobaczyć co zjada najwiecej pamięci i czasu procesora

2

Szklana kula mówi że jeśli dużo (dużo to nie 100 a milion) tych obiektów dodajesz i źle używasz to może być rzeczywiście problem.

0

@KamilAdam: ArrayList.add(new B());
To jak cały czas tak codaje i usuwam to obiekty dalej są w pamięci ?

1

@plugan300: to nie jest żaden anonimowy obiekt. I w jaki sposób to usuwasz?

1

A jak usuwasz? Bo tu masz tylko dodawanie.
Obiekty są w pamięci jeśli:

  1. Jest chociaż jedna referencja (bycie obiektu jako element w liście też jest liczone jako referencja)
  2. Jest 0 referencji ale GC jeszcze nie raczył się uruchomić i wszystkiego posprzątać

Pokaż więcej kodu bo teraz wróżymy :)

0

@ProgScibi: Arraylist.removeAll(podaje inna liste obiektów ktore ma usunąć), następnie listę która wskazywała obiekty do usunięcia, czyszczę metodą Arraylist.clear();

0

Możesz zastosować jakiś println debugging, tzn. wypisz sobie ile lista miała przed removeAll, ile chciałeś usunąć elementów i ile zostało. Wtedy zobaczysz czy to zadziałało tak jak chciałeś.

0

@Wibowit: teraz widzę że zużycie pamięci w menadżerze zadań cały czas rośnie, przy 350 MB już zaczyna ścinać.

0
plugan300 napisał(a):

@Wibowit: teraz widzę że zużycie pamięci w menadżerze zadań cały czas rośnie, przy 350 MB już zaczyna ścinać.

Po co nam to wiedzieć, skoro nie wiemy jakie rzeczy się znajdują tej pamięci? Weź zrób chociaż ten println debugging.

0

@Wibowit: cały czas, główna lista ma 1200, a lista wskazująca elementy do usunięcia ma 0 więc działa to poprawnie.

0

Kod pokaz

1

Zamiast zgadywać to odpalasz VisualVM, podpinasz do aplikacji i sortujesz sobie wykresy pamięci po klasie obiektu i będzie wiadomo co ci tam zajmuje za dużo miejsca. Dodatkowo "ścinanie" sugeruje raczej ze coś pali CPU jednak. Domyślnie java alokuje do 1/8 ramu który jest dostępny, więc musiałbyś mieć 3GB żeby 350 wyczerpało ci dostępny heap i GC zaczął mielić CPU próbujac coś zwolnić. To że zużycie pamięci rośnie nic nie znaczy, bo GC na starych obiektach może się odpalać bardzo rzadko.

0

@stivens:
Teraz spróbowałem wykluczyć metodę g.dispose(); i chyba chodzi lepiej

import Window_choice_fonts.MainParametrs;

import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;

public class PanelMatrix extends JPanel implements KeyListener, Runnable {
    private Random random = new Random();
    private Window window;
    private MainParametrs mainParametrs;

    private CopyOnWriteArrayList<Character> characters = new CopyOnWriteArrayList();
    private boolean running = true;
    Thread t;

    public PanelMatrix(Window window, MainParametrs mainParametrs) {
        this.window = window;
        this.mainParametrs = mainParametrs;

        addKeyListener(this);
        setFocusable(true);

        t = new Thread(this);
        SwingUtilities.invokeLater(t::start);
    }

    public void paint(Graphics g) {
        g.setColor(Color.BLACK);
        g.fillRect(0,0,getWidth(),getHeight());

        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        for(Character c : characters) {
            g.setFont(mainParametrs.getFont());
            g.setColor(c.color);
            g.drawString(String.valueOf(c.character), (int)c.pX, (int)c.pY);
        }
        //g.dispose();

    }

    @Override
    public void run() {
            long lastTime = System.nanoTime();
            ArrayList<Character> remove_characters = new ArrayList<>();

            final double speeds_arrays[] = new double[500];

            for(int i = 0; i < 500; i++) {
                int speed = random.nextInt(100);
                speeds_arrays[i] = ((double)speed) / 10;
            }

            int index = 0;

                while (running) {
                    if (lastTime + 1000000000l / mainParametrs.getRefreshCounter()< System.nanoTime()) {
                        lastTime = System.nanoTime();

                        index++;
                        if(index > 5) {
                            for (int i = 0; i < getWidth() / mainParametrs.getSpace_beetwen_character(); i++) {
                                characters.add(new Character(i *  mainParametrs.getSpace_beetwen_character(), 0, random, speeds_arrays[i]));
                            }
                            index = 0;
                        }

                        for (Character c : characters) {
                            c.pY+=c.speed;
                            c.refresh();
                            if (c.pY > getHeight()) {
                                remove_characters.add(c);
                            }
                        }
                        characters.removeAll(remove_characters);
                        remove_characters.clear();
                        repaint();

                        //System.out.println("głowna lista :" + characters.size() +", lista wzkazujaca elementy do usun." +remove_characters.size());

                    }
                }
            }

    @Override
    public void keyTyped(KeyEvent e) {

    }
    private boolean index = true;

    @Override
    public void keyPressed(KeyEvent e) {
        try {
            if (getGraphics() != null) {
                if (index) {

                }
            }
        }catch (ConcurrentModificationException ex) {}

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

            
    public class Character {
        private double speed;
        private char character;

        private double pX;
        private double pY;
        private Color color;

        private final int countersChange = random.nextInt(100)+1;
        private int counter = 0;

        public Character(double pX, double pY, Random random, double speed) {
            this.pX = pX;
            this.pY = pY;
            this.speed = speed;
            if(random.nextBoolean()) {
                character = (char) (random.nextInt(10) + 48);
            }else {
                character = ' ';
            }

            color = Color.GREEN;//new Color(random.nextInt(255), random.nextInt(255),random.nextInt(255));
        }

        public void refresh() {
            if(character == ' ') {return;}
            counter ++;
            if(counter > countersChange) {
                counter = 0;
                character = (char) (random.nextInt(10) + 48);
            }
        }
    }
}
1

Jak masz kolekcję swojego typu (Character), to usuwanie porównuje poprzez hash/equals. Jeśli wmiędzyczasie zmieniasz stan obiektów (a zmieniasz, bo jakieś pY i refresh robisz), to mój pierwszy strzał jest, że zmienił się hashCode i pewne obiekty zostają w kolekcji forever.

Spróbuj w klasie Character wygenerować (prawy przycisk myszy -> Generate -> Equals and hashcode) metody equals i hashCode, domyślnie tak jak IDE zaproponuje (czyli biorąc pod uwagę wszystkie pola klasy). I zobacz czy zadziałało.

1

Polecam doopa printing z wielkością kolekcji to się dowiesz czy elementy są usuwane. Ewentualnie można użyć debugera żeby to sprawdzić

2

Polecam doopa printing z wielkością kolekcji to się dowiesz czy elementy są usuwane.

Żaden doopa printing tylko slf4j

2

list.remove() usuwa referencję obiektu z listy, zakładając, że nie ma on żadnej innej referencji, kiedyś GC powinien się do niego dobrać. Oczywiście zakładając, że nic już na ten obiekt nie wskazuje, czyli np. nie ma na niego jakiejś jawnej referencji, nikt nie zarejestrował na tym obiekcie listenera, obiekt sobie nie odpalił w środku jakiegoś wątku ze wskazaniem na siebie i temu podobne. Samo działanie GC to potencjalnie powód do spowolnienia aplikacji. To czy obiekt został stworzony z klasy nazwanej, czy anonimowej nie ma znaczenia dla cyklu jego życia.

--- Edit Zajrzałem w większy kod.
t = new Thread(this); Przekazujesz tu referencję do wątku, który będzie się kręcił do momentu:
while (running)
Zmienną running inicjujesz wartością true, ale nigdy nie przestawiasz (albo tego nie widzę) na false, więc jest to pętla, która się nie skończy.
Aktywny wątek jest kotwicą GC, czyli GC go nie tyka, a tutaj przechowujesz w nim referencję do obiektu.

2

@plugan300:

Thread t;

public PanelMatrix(Window window, MainParametrs mainParametrs) {
    this.window = window;
    this.mainParametrs = mainParametrs;

    addKeyListener(this);
    setFocusable(true);

    t = new Thread(this);
    SwingUtilities.invokeLater(t::start);
}

Tak się nie robi :/ Konstrutory nie są od odpalania wiątkow.
Jak mas jakiś Runnable to nie powinien on zajmowac się odpalaniem samego siebie!

2

Nie ma czegoś takiego jak "anonimowy obiekt" w Javie. Najwyżej jest "anonimowa implementacja klasy lub interfejsu", ale to nie o to Ci chodzi.

0

@piotrpo: W innej klasie ustalam running na false i run się zakańcza, później tworze nowy wątek do tej samej referencji i running daje na true

2

Zmienna sterująca pętlą jest prywatna i nie masz w klasie żadnych metod dostępowych. Albo używasz refleksji, albo nie wiem co ustawiasz.

0

@piotrpo: bo nie zrobiłem jeszcze dalszej części kodu który będzie odpowiadał za zmiane wartości runnining, na tą chwilę mogłem wpisać nawet true

1

A ten hashcode/equals sprawdziłeś?

3
  1. class PanelMatrix extends JPanel implements KeyListener, Runnable masz jakiś limit na liczbę klas w programie ze wszystko wrzuciłeś do jednej? o_O
  2. catch (ConcurrentModificationException ex) {} doskonała obsługa błędów...
  3. Masz pętle która nie ma żadnego sleepa więc ten wątek będzie palić CPU tak szybko jak tylko da radę.
  4. Tak jak ktoś napisał wyżej, nigdzie nie masz dostępu do zmiennej running więc ten wątek na pewno nie jest ubijany. Nie wiem co i gdzie przestawiasz, ale na pewno nie to co myślisz.
3

To masz obiekt, który w konstruktorze tworzy sobie prywatny wątek i przekazuje samego siebie jako Runnable. Nie masz jak zatrzymać nieskończonej pętli. Tak długo jak metoda run działa (a działać będzie wiecznie) tak długo ten obiekt nie zostanie uprzątnięty przez GC. W obecnej formie (przedstawionej w poście) każdy taki obiekt, który stworzysz będzie zalegał na stercie do momentu zakończenia programu. Co gorsza co jakiś czas będzie coś tam robić, a co jeszcze gorsze, będzie trzymać referencje do obiektów które mu przekazujesz, czyli one również nie ulegną zniszczeniu.

0

@piotrpo: Link do filmiku z VisualVM - https://streamable.com/hf2ce1

1
plugan300 napisał(a):

@piotrpo: Link do filmiku z VisualVM - https://streamable.com/hf2ce1

No przecież @piotrpo Ci dokładnie opisał co się dzieje, masz podane jak na tacy wszystko.

3

@plugan300: Nie wiem czy to jest ten najistotniejszy który spowalnia ci aplikację, ale jestem pewien, że to jest bląd. Dodatkowo zmień if(jakieś tam sprawdzanie czasu) na t.sleep() inaczej ta pętla będzie ci latać ciągle, co kopie CPU (tak jak pisał @Shalom )

1 użytkowników online, w tym zalogowanych: 0, gości: 1