Blog (7)
Komentarze (49)
Recenzje (0)
@jullo89Profiler w Netbeans

Profiler w Netbeans

Jest wolna chwila, jest i wpis na blogu – tym razem o wydajności programów, a raczej o sprawdzaniu wydajności programów w środowisku Netbeans. Służy do tego Profiler, który dostarcza nam wielu cennych informacji o tworzonej przez nas aplikacji.

Ale co mamy sprawdzić? W tym przypadku możemy sprawdzić głównie wydajność programów pod względem zużycia pamięci, monitorowanie stanu wątków, wydajność procesora oraz możemy znaleźć „wąskie gardła”. Ja zwracam bardziej uwagę na możliwość monitorowania wątków i zużycie pamięci niż na wydajność procesora, ponieważ w tym przypadku wyniki są często (zawsze) bardzo przekłamane. Ale nie ma co się dziwić, ponieważ środowisko też potrzebuje czasu procesora na zebranie informacji o stanie pamięci, wątkach, załadowanych obiektach i klasach.

Do czego służy Profiler? Do poprawy naszej aplikacji, aby była wydajniejsza, doskonalsza, lepsza – tworząc programy/systemy należy dążyć do doskonałości chociaż już na starcie wiadomo, że jej nie osiągniemy. Dzięki temu narzędziu możemy się dowiedzieć jak wykonują się nasze wątki, ile czasu się wykonują, spędzają w trybie monitora lub są uśpione. Otrzymamy również informacje o ilości instancji obiektów poszczególnych klas oraz o tym ile pamięci zużywa nasza aplikacja. Większość informacji otrzymamy w formie wykresu lub tabelki. Wszystko ładnie, przejrzyście i zrozumiale.

Niestety nie poprawi za nas aplikacji. Może i to lepiej... więcej pracy dla programistów i projektantów(Ci też powinni być przy „profilowaniu” aplikacji).

Hmm co jeszcze... Może to, że tworząc program w Netbeans`ie możemy wyznaczać miejsca, w których chcemy uzyskać szczegółowych informacji o stanie pamięci i programie tj. sprawdzić ilość obiektów poszczególnych instancji itp. . Uruchamiając Profiler możemy określić co nas interesuje i jakie informacje mają być zbierane podczas wykonania tego procesu.

Dla zaawansowanych użytkowników Netbeansa istnieje możliwość uruchamiania aplikacji na innym komputerze co skutkuje szybszym wykonywanie procesu i zalecane jest dla dużych systemów z wiadomych przyczyn.

Tradycyjnie już skromny przykład

Zaczniemy od stworzenia wielowątkowej aplikacji- w tym wypadku aplikacja serwera. W tym celu otwieramy Netbensa, tworzymy nowy projekt. Nasza aplikacja będzie się składała z trzech klas: główna klasa z metodą "main", klasa będąca wątkiem serwera oraz klasa wątków obsługujących klientów.

Mój projekt wygląda mniej więcej tak, jak na obrazku poniżej.

Projekt
Projekt

Na początek kod wątku obsługi klienta ”ClientThread.java”.



package profilertest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author michal
 */
public class ClientThread extends Thread{
    final private Vector clients;
    private Socket s;
    private static int id=0;
    
    public ClientThread(Socket s, Vector c){
        super("ClientThread"+id);
        id++;
        this.s=s;
        this.clients=c;
    }
    
       @Override
    public void run() {
        BufferedReader reader = null;
        BufferedWriter writer = null;
        try {
            reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
            writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            String line = reader.readLine();
            writer.write(line + "\r\n");
            writer.flush();
            synchronized (clients) {
                clients.remove(this);
            }
        } catch (IOException ex) {
            Logger.getLogger(ClientThread.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                reader.close();
                writer.close();
                this.finalize();
            } catch (IOException ex) {
                Logger.getLogger(ClientThread.class.getName()).log(Level.SEVERE, null, ex);
            } catch (Throwable ex) {
                Logger.getLogger(ClientThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}

Jak widać nic skomplikowanego klasa ta nie robi, ot po prostu odbiera napis od klienta i odsyła go, a na końcu usuwa obiekt z listy.

Poniżej przedstawiony został kod klasy ServerThread. Również za dużo się nie dzieje w tej klasie. Zwyczajnie akceptowane jest połączenie od klienta, tworzony wątek obsługi klienta i dodanie go do listy.


	package profilertest;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author michal
 */
public class ServerThread extends Thread {

    ServerSocket s;
    final int port = 16969;
    final Vector<ClientThread> clients = new Vector<ClientThread>();

    public ServerThread() {
        super("ServerThread");
    }

    @Override
    public void run() {
        try {
            System.out.println("Serwer wystartował");
            s = new ServerSocket(port);                             // tworzymy nowe gniazdo serwera
            while (true) {
                Socket s1 = s.accept();                             //akceptujemy nowe połączenie
                ClientThread ct1 = new ClientThread(s1, clients);   //tworzymy wątek klienta
                synchronized(clients){
                    clients.add(ct1);                               //dodajemy klienta do wektora
                }
                ct1.start();                                        //uruchamiamy wątek obsługi klienta
                System.out.println("Dodałem klietna");
            }
        } catch (IOException ex) {
            Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(-1);
        }
    }
}

Główna klasa z metodą „main” wygląda następująco:


	package profilertest;

public class ProfilerTest {
    
    public static void main(String[] args) {
       ServerThread server = new ServerThread();
       server.start();
    }
}

Chyba najdłuższa klasa w moim życiu :P Jak już mamy stworzony projekt serwera, to trzeba coś zrobić, aby przetestować nasz serwer i pokazać jak się sprawdza wydajność aplikacji w Netbeansie. Do tego tworzymy nowy projekt, z jedną klasa z metodą main. Kod został przedstawiony poniżej.


package profilerclient;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author michal
 */
public class ProfilerClient {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Vector<Thread> v = new Vector<Thread>();
        for(int i=0; i<100; i++){
            
           Thread t1 = new Thread("Client: "+i){//tworzymy nowy wątek
              public void run(){
                  BufferedReader reader = null;
                  BufferedWriter writer = null;
                    try {
                        Socket s = new Socket("localhost",16969);//łączymy się z serwerem
                        writer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                        reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
                        writer.write(getName()+"\r\n");//wysyłamy nazwę wątku
                        writer.flush();
                        System.out.println(reader.readLine());//wypisujemy odebrany napis
                        reader.close();
                        writer.close();
                    } catch (UnknownHostException ex) {
                        Logger.getLogger(ProfilerClient.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IOException ex) {
                        Logger.getLogger(ProfilerClient.class.getName()).log(Level.SEVERE, null, ex);
                    }
              }//koniec metody run  
            };
            v.add(t1);
        }//koniec pętli for
        
        for(int i=0; i<100; i++){
            v.elementAt(i).start();//startujemy wątek i-ty
        }
    }
}

Po skończeniu pracy z kodem powinniśmy otrzymać coś takiego lub podobnego jak na zrzucie poniżej.

Projekty-pliki
Projekty-pliki

Ok. Więc kod programu mamy już gotowy. Co teraz należy zrobić? Rozpocząć proces profilowania. Warto przed tym dokonać kalibracji środowiska JDK. W tym celu należy wybrać z menu „Profile” ->”Advanced Commands”-> „Run Profiler Calibration”, wybrać wersję JDK, którą używamy.

Kalibracja
Kalibracja
Wybór JDK
Wybór JDK

Następnie zaznaczamy nasz projekt jako główny(PPM, z menu kontekstowego wybieramy „Set as Main Project”) i z menu wybieramy „Profile” -> „Profile Main Project”. Powinno otworzyć się okno konfiguracyjne, w którym zaznaczamy interesujące opcje. Ja zrobiłem to tak, jak na zrzutach poniżej.

Ustawienia
Ustawienia
Ustawienia
Ustawienia
Ustawienia
Ustawienia

Przyszedł czas na uruchomienie przyciskiem „Run”. Po tym kroku po prawej stronie powinna pokazać się zakładka „Profiler”. Jeżeli z jakiegoś powodu nie pokazała się, można ją włączyć w menu „Windows”-> „Profiling”->”Profiler Control Panel”.

Profiler Control Panel
Profiler Control Panel

W panelu tym możemy zarządzać procesem sprawdzania aplikacji serwera, włączyć monitorowanie wątków, zużycie pamięci, pobierać „zrzuty pamięci” etc. . Zostało to przedstawione na rysunkach poniżej.

Przebieg wątków
Przebieg wątków
Wątki - Tabela
Wątki - Tabela
Wątek- Szczegóły
Wątek- Szczegóły

Trzeba pamiętać, że sprawdzając aplikację serwera trzeba uruchomić aplikację klienta. Na wykresie przebiegu wątków widać jak tworzone i uruchamiane były wątki obsługi klienta.

Pamięć - Stos
Pamięć - Stos
Wątki i załadowane klasy
Wątki i załadowane klasy
Pamięć
Pamięć

Jak zostało to pokazane można wiele się dowiedzieć z wykresów i tabel o wykonywaniu naszego programu. Ale to nie wszystko...

Podczas wykonywania programu tym sposobem możemy pobierać zrzuty pamięci przyciskiem „Take Snapshot” lub obserwować wyniki na żywo (jak ktoś potrafi szybko analizować :P). Dodatkowo możemy w programie ustawić miejsca w których ma się wykonać „snapshot”.

Jak to zrobić? Przechodzimy do kodu programu, klikamy prawym klawiszem myszy i z menu kontekstowego wybieramy „Profiling”->„Insert Profiling Point”. Pokaże się okno dodawania, w którym ustawiamy wszystkie parametry i kończymy dodając miejsce zrzutu. W jednej linijce możemy dodać 2 miejsca – przed wykonaniem instrukcji/metody i po wykonaniu. Wykorzystuję to do sprawdzania dużych metod, które długo się wykonują.

Dodawanie punktu zrzutu
Dodawanie punktu zrzutu
Ustawienia punktu
Ustawienia punktu
Ustawienia punktu
Ustawienia punktu

Następnie podczas wykonania procesu profilowania w oknie „Profiling Points” (jeżeli nie jest ono widoczne, należy je włączyć w menu: „Windows” -> „Profiling” -> „Profiling Points”), zostanie pokazany nasz punkt i przechwycony zrzut. Można go otworzyć klikająć PPM i wybierając opcję „Show Report”.

Profiling Points
Profiling Points

Trochę długi wpis wyszedł ze względu na zamieszczony kod programów oraz zrzuty ekranów. Dla osób szerzej zainteresowanych polecam strony: http://netbeans.org/features/java/profiler.html http://profiler.netbeans.org/ http://netbeans.org/kb/docs/java/profiler-intro.html Dziękuję za uwagę ;)

Wybrane dla Ciebie
Komentarze (54)