2009-07-05 11 views
14

Sono nuovo alla programmazione Java ma un programmatore C++ esperto. Stavo imparando come programmare le GUI usando swing. Mi stavo chiedendo quanto siano intensi in termini di risorse (runtime e memoria) ActionListeners? Esiste una linea guida generale sul numero totale di ascoltatori che si dovrebbero creare in un particolare programma? Quanti ne verranno influenzati fino alla prestazione?In che modo le risorse sono ascoltanti in java?

Attualmente sto imparando Java attraverso il libro Deitel Developer Series Java per programmatori. In un particolare esempio hanno una serie di JRadioButtonItems come variabile privata per la classe. Hanno anche creato una classe ItemHandler estesa dalla classe ActionListener che ha condotto una ricerca lineare sull'intera serie di pulsanti di opzione per determinare quella selezionata e modifica di conseguenza lo stato del programma. Tutti i pulsanti di opzione nell'array condividevano lo stesso listener di azioni. Ciò mi è sembrato piuttosto inefficiente per condurre una ricerca lineare delle informazioni, quindi ho riscritto la classe ActionListener per prendere il valore proposto da modificare nel costruttore e ho dato ad ogni pulsante radio il proprio ActionListener con il valore proposto passato dal costruttore per evitare facendo una ricerca lineare. Quale metodo sarebbe migliore per quanto riguarda le prestazioni? Mi scuso per sembrare un noob, sto solo cercando di sviluppare un buon set di abitudini per la programmazione in Java. In allegato c'è un piccolo esempio del codice. Grazie.

/************************************************************************ 
    Original code in Deitel book with linear search of selected Radio button in Actionlistener 
    ****************************************************************************/ 
    import java.awt.Color; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.ButtonGroup; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JMenu; 
import javax.swing.JMenuBar; 
import javax.swing.JMenuItem; 
import javax.swing.JRadioButtonMenuItem; 


public class MenuTest extends JFrame{ 
    private final Color colorValues[] = {Color.BLACK, Color.WHITE, Color.GREEN}; 
    private JRadioButtonMenuItem colorItems[];  
    private ButtonGroup colorButtonGroup; 


    public MenuTest(){ 
     super("Menu Test"); 
     JMenu fileMenu = new JMenu("File"); 

     JMenuBar bar = new JMenuBar(); 
     setJMenuBar(bar); 
     bar.add(fileMenu); 

     String colors[] = {"Black", "White", "Green"}; 
     JMenu colorMenu = new JMenu("Color"); 
     colorItems = new JRadioButtonMenuItem[colors.length]; 
     colorButtonGroup = new ButtonGroup(); 

     ItemHandler itemHandler = new ItemHandler(); 

     for(int count = 0; count < colors.length; count++){ 
      colorItems[count] = new JRadioButtonMenuItem(colors[count]); 
      colorMenu.add(colorItems[count]); 
      colorButtonGroup.add(colorItems[count]); 
      colorItems[count].addActionListener(itemHandler); 
     } 

     colorItems[0].setSelected(true); 
     fileMenu.add(colorMenu); 
     fileMenu.addSeparator(); 

    } 

    private class ItemHandler implements ActionListener{ 
     public void actionPerformed(ActionEvent event){ 
      for(int count = 0; count < colorItems.length; count++){ 
       if(colorItems[count].isSelected()){ 
        getContentPane().setBackground(colorValues[count]); 
       } 
      } 
     } 
    } 


    public static void main(String args[]){ 
     MenuTest menuFrame = new MenuTest(); 
     menuFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     menuFrame.setSize(600,400); 
     menuFrame.setVisible(true); 
     menuFrame.getContentPane().setBackground(menuFrame.colorValues[0]); 
    } 
} 
    /************************************************************************ 
    My Code redefined version of Deitel's w/o linear search in ActionListener 
    ************************************************************************/ 

     import java.awt.Color; 
     import java.awt.event.ActionEvent; 
     import java.awt.event.ActionListener; 

     import javax.swing.ButtonGroup; 
     import javax.swing.JFrame; 
     import javax.swing.JLabel; 
     import javax.swing.JMenu; 
     import javax.swing.JMenuBar; 
     import javax.swing.JMenuItem; 
     import javax.swing.JRadioButtonMenuItem; 

     public class MenuTest extends JFrame{ 
     private final Color colorValues[] = {Color.BLACK, Color.WHITE, Color.GREEN}; 
     private JRadioButtonMenuItem colorItems[];  
     private ButtonGroup colorButtonGroup; 


     public MenuTest(){ 
      super("Menu Test"); 
      JMenu fileMenu = new JMenu("File"); 

      JMenuBar bar = new JMenuBar(); 
      setJMenuBar(bar); 
      bar.add(fileMenu); 

      String colors[] = {"Black", "White", "Green"}; 
      JMenu colorMenu = new JMenu("Color"); 
      colorItems = new JRadioButtonMenuItem[colors.length]; 
      colorButtonGroup = new ButtonGroup(); 

      ItemHandler itemHandler = new ItemHandler(); 

      for(int count = 0; count < colors.length; count++){ 
       colorItems[count] = new JRadioButtonMenuItem(colors[count]); 
       colorMenu.add(colorItems[count]); 
       colorButtonGroup.add(colorItems[count]); 
       colorItems[count].addActionListener(new ItemHandler(colorValues[count])); 
      } 

      colorItems[0].setSelected(true); 
      fileMenu.add(colorMenu); 
      fileMenu.addSeparator(); 

     } 

     private class ItemHandler implements ActionListener{ 
      private Color setColor; 
      public ItemHandler(Color inColor){ 
       super(); 
       setColor = inColor; 
      } 
      public void actionPerformed(ActionEvent event){ 
       getContentPane().setBackground(setColor); 
       repaint(); 
      } 
     } 
     public static void main(String args[]){ 
      MenuTest menuFrame = new MenuTest(); 
      menuFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      menuFrame.setSize(600,400); 
      menuFrame.setVisible(true); 
      menuFrame.getContentPane().setBackground(menuFrame.colorValues[0]); 
     } 
    } 

risposta

17

Utilizzo CPU: vicino a nessuno. Gli ascoltatori vengono chiamati solo quando lo stato dell'oggetto che ascoltano viene modificato. Loro non "ascoltano" veramente. L'oggetto a cui stanno ascoltando li chiama quando necessario. (Nota: questa è una semplificazione)

Utilizzo memoria: Principalmente un ActionListener non ha stato. Quindi l'utilizzo duraturo della memoria è il minimo richiesto per qualsiasi oggetto. Nel tuo esempio, lo è lo stato, poiché hai un campo setColor. Ma l'utilizzo della memoria è basso.

IMO, gli ascoltatori sono efficienti e puoi utilizzarne quanti ne vuoi.

4

Gli ascoltatori sono molto economici rispetto ai componenti a cui sono collegati. È improbabile che avere centinaia di ascoltatori faccia molta differenza per CPU o memoria. Tuttavia, il totale degli ascoltatori di lavoro è più importante. Se si riscontra un problema di prestazioni, è possibile utilizzare un profiler CPU/memoria per identificare e ottimizzare la causa. Non è qualcosa di cui dovresti preoccuparti troppo in anticipo.

1

Ricordare che l'ascolto non è lo stesso di un'attesa impegnata. L'ascoltatore è registrato su un elenco di parti interessate tramite metodi proxy di qualche tipo. C'è molto poco tempo di CPU sprecato nella maggior parte dei Pattern Listener.

1

C'è una tradizione in Java ME per utilizzare meno classi, poiché il sovraccarico di download di un altro file di classe è significativo, quindi a volte si ottiene codice Java che tenta e riduce il numero di tipi di listener e utilizza la logica condizionale nel listener. Per le dimensioni delle applicazioni Java desktop non è davvero un problema, ma a volte si vede che perde in quello spazio.

Non proprio in risposta alla domanda, ma una volta che sei andato così lontano, scoprirai che puoi ridurre l'ambito della maggior parte dei campi di MenuTest come variabili di metodo nel costruttore.

Io tendo ad utilizzare gli ascoltatori anonimi, poiché la maggior parte delle volte sono troppo semplici per dare fastidio ai nomi.

Il codice visualizzato utilizza i listener di azioni per rilevare se le voci di menu sono "attivate".

Nel costruttore imposta la voce di menu selezionata, quindi nel metodo principale accede al campo colorValues dell'oggetto e imposta il valore dello sfondo. Ovviamente questo fallisce sia nel nascondere l'informazione che nel comportamento incapsulante.

Se invece si utilizza ChangeListener s per verificare se una voce di menu è impostata o meno come selezionata, allora non si hanno due parti separate della logica per mantenere lo stesso stato del colore impostato in risposta alla chiamata a setSelected e non è necessario intercettare il campo colorValues nel chiamante.

for (int count = 0; count < colors.length; count++){ 
    colorItems[count] = new JRadioButtonMenuItem(colors[count]); 
    colorMenu.add(colorItems[count]); 
    colorButtonGroup.add(colorItems[count]); 

    final Color color = colorValues[count]; 

    colorItems[count].addChangeListener (new ChangeListener() { 
     public void stateChanged (ChangeEvent event){ 
      if (((AbstractButton) event.getSource()).isSelected()) 
       getContentPane().setBackground (color); 
     } 
    }); 
} 

colorItems[0].setSelected(true); 

// no call to setBackgroundColour() in main() 

si può anche avere una seconda variabile finale per tenere il colorItem ed evitare il cast sul event.getSource().

Sarebbe anche possibile implementare l'ascoltatore di modifiche per ascoltare anche le modifiche che impostano il colore di sfondo sul fotogramma e selezionare la voce di menu appropriata se il colore viene modificato utilizzando setBackground().

La chiamata setBackground() contrassegna automaticamente il pannello come danneggiato, quindi non è necessario richiamare il ridisegno in seguito.

0

Bene, considera l'implementazione listener più semplice: tutto ciò che stai facendo è fondamentalmente aggiungere un oggetto a un elenco di Listener, niente di più. Registrazione di una funzione di callback, se lo si desidera. Così l'attuazione di questa linea:

someObject.addListener(new someAnonymousListener{ ... }); 

essenzialmente sarebbe:

someListenerList.add(new someAnonymousListener{ ... }); 

Fino ad ora, nessun danno nessun fallo. L'oggetto è seduto lì in una lista con tutti gli altri ascoltatori registrati.

Tuttavia, notate che cosa accade quando un evento viene attivato sull'oggetto:

for (SomeListener l : someListenerList) { 
    l.doSomeAction(); 
} 

Yikes. Questo è dove dovresti stare attento. Assicurati che l'ascoltatore non stia facendo nulla di troppo complesso o vedrai un collo di bottiglia, che verrà visualizzato durante la profilazione.

Bottom line: semplicemente tenendo un listener su un oggetto è quasi gratuito. Assicurati solo di non registrare troppi ascoltatori e che ognuno di loro sta mantenendo le sue azioni semplici e precise.

+0

si alza un buon problema: gli ascoltatori dovrebbero generalmente fare ben poco. Se hanno bisogno di fare qualcosa di complesso, dovrebbero generare un nuovo thread, idealmente usando la classe SwingWorker, quindi tornare immediatamente. –

4

Il problema più grande qui è il tempo impostato e, in misura minore, la memoria di generazione permanente.

Per le dimensioni effettive degli oggetti è possibile eseguire una retro dell'analisi dell'impatto di una busta. Circa 20 byte di un oggetto. Supponi di averne 10.000, ovvero 200.000. Su una macchina da 1 GB che è lo 0,02% della memoria della tua macchina in ascolto, non mi preoccuperei di ciò in un cellulare.

L'immagine più grande è il numero di classi caricate. Il mio esperimento (su Usenet da qualche parte) ha dato circa 2K di overhead per ogni relativamente piccola classe interna anonima caricata. 10.000 di questi sono 20 MB. 2% della nostra macchina da 1 GB. Mi preoccuperei di questo per un software ampiamente distribuito, ma non per una dozzina di macchine in un'azienda o un reparto di medie dimensioni. Anche così, è vagamente più importante ottenere software che funzioni effettivamente ed è gestibile (a quanto pare, l'85% dei costi di sviluppo del software è in manutenzione (per alcune definizioni) e la maggior parte dei progetti non arriva mai così lontano).

Ottenere il codice caricato è un'operazione relativamente costosa. Anche se fortunatamente ora abbiamo dei barattoli anziché aprire un nuovo socket per fare una richiesta HTTP 1.0 per ogni file di classe (le applet sono state lente all'inizio - come è successo?). Tuttavia, le stringhe devono essere controllate, risolte dal metodo, verificate dal codice, ecc. Ed è ancora in fase di interpretazione per 1.500 iterazioni prima di sostenere le spese del compilatore.

Dopo aver lavorato (non funziona fino a quando non è in produzione), software gestibile, quindi si potrebbe voler prendere in considerazione le prestazioni di ottimizzazione (anche se, per essere contraddittori, è bene avere un heads-up). Accorda la parte più lenta. Con i processori in GHz, rispondere a ciascun piccolo clic del mouse tende ad andare più che abbastanza veloce. Lento tende ad essere operazioni come aprire una nuova finestra di dialogo.

Quindi, per noi, vogliamo ridurre i tempi di impostazione, ma possiamo essere abbastanza inefficienti in caso di incendio. 50 ms è il tempo di risposta a cui dovremmo mirare, e quello su una macchina da 1 GHz che è di 20.000.000 di cicli (lotti!). Quindi una strategia opportunamente performante consiste nell'usare pochi tipi di ascoltatori. Utilizzare un singolo tipo di listener (possibilmente istanza) per ascoltare una discreta quantità di modello. Ignora gli argomenti dell'evento (di solito è una buona idea per un listener di modifiche di stato) ed esegui il controllo di ciò che deve essere fatto (ricorda che abbiamo un sacco di tempo).

Ho bisogno di ripetermi qui. Rendi il tuo codice carino. Vai per molti ascoltatori di input (e comandi). Gli ascoltatori del cambiamento di stato dovrebbero ignorare i loro argomenti.

+0

10.000 oggetti a 20 byte! = 20 K (dovrebbe essere 200 K). –

+0

@Kevin Brock Erm, si. D'oh. 0,02%. –

2

Sono un programmatore di swing da 5 anni. Secondo la mia esperienza, non è mai un problema come possono esserci gli ascoltatori. È importante annullare la registrazione degli ascoltatori quando non sono più interessati agli eventi.

Ricordare solo che nelle applicazioni GUI il tempo di risposta è più importante della memoria. Si può essere interessati nei miei articoli:

Use Weak Listeners to avoid Memory Leaks
Know when your Object gets Garbage Collected
Long-Lived Models may cause Memory Leaks

Problemi correlati