Performances des Getters/Setters pour les JavaBeans

Afin d’accéder aux variables d’instances d’un objet, on n’appelle rarement directement ces variables: on utilise des accesseurs/mutateurs ou Getter/Setter. Nous allons tenter de voir si ce type de pattern, aussi vieux que l’objet, est pénalisante.

Qu’est ce qu’un JavaBean ?

(Source: Wikipedia)

Un composant JavaBean est une simple classe Java qui respecte certaines conventions sur le nommage des méthodes, la construction et le comportement. Le respect de ces conventions rend possible l’utilisation, la réutilisation, le remplacement et la connexion de JavaBeans par des outils de développement.

Les conventions à respecter sont les suivantes :

  • La classe doit être « Serializable » pour pouvoir sauver et restaurer l’état d’instances de cette classe ;
  • La classe doit posséder un constructeur sans argument ;
  • Les propriétés de la classe (Variables d’instances) doivent être accessibles via des méthodes suivant elles aussi des conventions de nommage ;
  • La classe doit contenir les méthodes d’interception d’événements nécessaires.

Exemple de Javabean:

// PersonBean.java
 
public class PersonBean implements java.io.Serializable {
    private String name;
    private boolean deceased;
 
    // Constructeur par défaut (Ne prenant pas d'arguments).
    public PersonBean() {
    }
 
    public String getName() {
        return (this.name);
    }
    public void setName(String name) {
        this.name = name;
    }
    /* Différentes sémantiques pour les booléens. (get vs is)
       Ici nous optons pour un préfixe is. */
    public boolean isDeceased() {
        return (this.deceased);
    }
    public void setDeceased(boolean deceased) {
        this.deceased = deceased;
    }
}

Il existe des critiques à ce genre d’architecture comme l’explique Allen Holub sur JavaWorld. L’utilisation de mutateurs et d’acesseurs ne ferait que complexifier le code, sans y apporter de réelle souplesse. Il irait même contre les principes d’Extreme Programming qui demande du code simple, sans forcement se soucier du lendemain

Procédure de tests

Nous allons principalement comparer les performances d’accès à une variable d’instance en lecture et en écriture.

On distingue deux sortes de variables: les primitives et les objets.

Il existe 3 sortes d’écritures, l’écriture directe en utilisant la variable d’instance public et l’écriture via un accesseur, avec utilisation ou non du mot clef final sur l’argument.

Il existe 2 sortes de lectures, la lecture directe en utilisant la variable d’instance public et l’écriture via un mutateur.

Je vais tester ces différents cas avec un Athlon 3000+ sous Windows XP 32 bits SP3 et Java 6. Je m’assurai durant l’expérience que le Garbage Collector n’est pas appelé.

Résultats

Les tests pour chaque type d’instruction ont été répété 1 milliard de fois sur cette classe instanciée:

/**
 * Notre JavaBean de test.
 */
class TestObjectBean implements Serializable {
 
	/** accés direct à la primitive. */
	public int myPrimitive;
 
	/** accés direct à l'objet. */
	public Object myObject;
 
	/** Getter d'accés à la primitive. */
	public int getMyPrimitive() {
		return myPrimitive;
	}
 
	/** Setter avec final pour une primitive. */
	public void setFinalMyPrimitive(final int myPrimitive) {
		this.myPrimitive = myPrimitive;
	}
 
	/** Setter sans final pour une primitive. */
	public void setMyPrimitive(int myPrimitive) {
		this.myPrimitive = myPrimitive;
	}
 
	/** Getter d'accés à l'objet. */
	public Object getMyObject() {
		return myObject;
	}
 
	/** Setter sans final pour un objet. */
	public void setMyObject(Object myObject) {
		this.myObject = myObject;
	}
 
	/** Setter avec final pour un objet. */
	public void setFinalMyObject(final Object myObject) {
		this.myObject = myObject;
	}
}

On constate que l’utilisation de final, rend le mutateur légèrement plus rapide pour les objets. Le contenu de la variable ou la référence de l’objet n’a pas besoin d’être recopié en utilisant final. Toutefois les conditions de tests ne permettent pas de conclure, avec un si petit écart, que l’utilisation des accesseurs et des mutateurs sont réellement pénalisant d’un point de vue des performances. Pour ma part, je continuerai à les utiliser 😉

Ces chiffres ne sont pas parfaits. Bien que l’exécution de ce Benchmark a été effectué sur un OS qui était le moins chargé possible, certaines expériences rapportaient que la lecture directe était plus lente que de passer par un accesseur!!!! Cela me renforce dans l’idée que l’impact niveau performance est infinitésimale.

A lire:

Vous pouvez télécharger la classe Java utilisée pour faire ce Benchmark.