2016-01-21 26 views
5

Si è imbattuto in questo mentre faceva un refactoring. Le chiamate a getProperties() stavano causando un picco nell'utilizzo della CPU. Quello che abbiamo scoperto è che se si ha un getter senza un attributo associato, quando si effettua una chiamata a getProperties() quel getter viene chiamato oltre 1000 volte. La soluzione/soluzione è ovvia e sappiamo che ha qualcosa a che fare con la metaprogrammazione, ma perché questo sta accadendo (quale punto nella fonte groovy)? Vedere il codice di script groovy di seguito:Groovy getProperties() chiama invocando getter per attributo inesistente oltre 1000 volte

class tester { 

    int count = 0 

    public getVar() { 
     println count++ + " getVar() called!" 
     return var 
    } 
} 

def t = new tester() 

t.getProperties() 

println "done!" 

Si dovrebbe vedere getVar() chiamato oltre 1000 volte. 1068 per essere precisi per noi.

+1

piuttosto strano. Ho appena provato questo nella console web groovy http://groovyconsole.appspot.com/ e lo vedo eseguito 110 volte. –

+9

Succede a causa di questa riga 'return var'. Questo in realtà sta invocando 'getVar()' stesso in ricorsione perché 'return var' è uguale a invocare' return getVar() '. Stampa il conteggio fino a quando la pila non viene traboccata. – dmahapatro

+2

'return var' è il problema. –

risposta

0

La domanda è probabilmente già stata risolta nei commenti, ma ho scavato un po 'più in profondità per rispondere anche alla parte "che punto nella fonte groovy".

Quando si chiama getProperties() nell'istanza di tester Groovy farà la sua magia ed infine chiamare DefaultGroovyMethods#getProperties(Object) che (in Groovy 2.4.7) si presenta così:

public static Map getProperties(Object self) { 
    List<PropertyValue> metaProps = getMetaPropertyValues(self); // 1 
    Map<String, Object> props = new LinkedHashMap<String, Object>(metaProps.size()); 

    for (PropertyValue mp : metaProps) { 
     try { 
      props.put(mp.getName(), mp.getValue()); // 2 
     } catch (Exception e) { 
      LOG.throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e); 
     } 
    } 
    return props; 
} 

In primo luogo, Groovy determina le proprietà meta del oggetto dato (vedi 1). Ciò restituirà tre proprietà:

  • var: getter solo (getVar()), senza setter, nessun campo
  • class: getter solo (ereditato da Object), senza setter, nessun campo
  • count: getter, setter (entrambi generati da Groovy) e campo

È possibile verificare facilmente chiamando t.getMetaPropertyValues().

Successivamente, Groovy cerca di ottenere il valore corrente di ogni proprietà e lo inserisce in una mappa (vedere 2). Quando raggiunge var, ricorda che var ha un getter (ovvero getVar()) e lo chiama. getVar() tuttavia restituisce var nuovamente. Per Groovy, questa è la stessa proprietà esatta determinata nel primo passaggio. Ancora una volta, chiama il suo getter getVar() e inizia il ciclo infinito.

Ad un certo punto, a seconda della JVM, questo si traduce in un StackOverflowError, che è esattamente ciò che questo sito è su tutti :-D