Embedding Tomcat 7 for unit tests

Recently (in fact, few months ago), I have had to develop a small REST service exposing some methods that could be invoked remotely by a REST client (using GET and POST methods). The service contract was so simple that I took less time to write the service class by using Jersey that declaring all the Spring dependencies in the project pom.xml or creating a new application with the Play! framework.

As a not fully agile developer, I didn’t write the tests before starting the service (oh !! you don’t respect the TDD ? no ! I don’t — unless for this time — :)). So after finishing the few lines of code of my service (<500 lines), came the time to test my service.

Then I was wondering myself how could make quickly an integration test in my JUnit test case inside my IDE, before creating a new Jenkins Job (yes ! this time I’ve heard about the CI :)). Below some of them :

  • Servlet Unit test
  • using Arquilian
  • starting manually an external servlet container
  • starting a container with Cargo
  • using an embedded server started during the test setup phase (@Before for instance)
  • etc…

As a lazzy developer as usual, I opted for the simpliest choice (for me) : using an embedded Tomcat server.

First, I added the following line (dependencies management) in my project pom.xml. Yes! here the project is a Maven based project.


<properties>
	<tomcat-version>7.0.26</tomcat-version>
</properties>
...
<!-- TEST -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-core</artifactId>
			<version>${tomcat-version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-logging-juli</artifactId>
			<version>${tomcat-version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<version>${tomcat-version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat-jasper</artifactId>
			<version>${tomcat-version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat-jasper-el</artifactId>
			<version>${tomcat-version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat</groupId>
			<artifactId>tomcat-jsp-api</artifactId>
			<version>${tomcat-version}</version>
			<scope>test</scope>
		</dependency>

And then, here comes the code… :)

Please note that there are so many different ways to achieve the same goal, therefore my following code is provided  for the example purpose only. Of course, you can write your classes by using your own style :)

First, I wrapped the Tomcat inside a Runnable that will be started during the test init phase

//import are removed
public class EmbeddedServer implements Runnable {

	private Tomcat	tomcat;
	private Thread	serverThread;

	public EmbeddedServer(int port, String contextPath) throws ServletException {
		tomcat = new Tomcat();
		tomcat.setPort(port);
		tomcat.setBaseDir("target/tomcat");
		tomcat.addWebapp(contextPath, new File("src/main/webapp").getAbsolutePath());
		serverThread = new Thread(this);

	}

	public void start() {
		serverThread.start();
	}

	public void run() {
		try {
			tomcat.start();
		} catch (LifecycleException e) {
			throw new RuntimeException(e);
		}
		tomcat.getServer().await();
	}

	public void stop() {
		try {
			tomcat.stop();
			tomcat.destroy();
			deleteDirectory(new File("target/tomcat/"));
		} catch (LifecycleException e) {
			throw new RuntimeException(e);
		}
	}

	void deleteDirectory(File path) {
		if (path == null) return;
		if (path.exists()) {
			for (File f : path.listFiles()) {
				if (f.isDirectory()) {
					deleteDirectory(f);
					f.delete();
				} else {
					f.delete();
				}
			}
			path.delete();
		}
	}

Finally run my JUnit tests where my client could request my REST service that has been previously deployed on the embedded Tomcat instance

public class MyAppDeployedOnLocalTest {

	@Before
	// or @BeforeClass
	public void startServer() throws ServletException {
		embeddedServer = new EmbeddedServer(9090, "/myservice");
		embeddedServer.start();

	}

	@After
	// or @AfterClass
	public void stopServer() {
		embeddedServer.stop();
	}

	public void test_send_request_without_parameters (){
            Client client = ...
            Reply reply = client.sendRequest();
            assertTrue (reply.isOk());
    }
}

And ..voilà ! :)

Paris JUG Juillet 2012 – Couchbase

Mardi 3 Juillet s’est déroulé le Paris JUG. Cette session était orientée 100% NoSQL.

La première partie fut animée par Raghavan « Rags » N. Srinivas sur une présentation de Couchbase.


Attention : on parle bien de Couchbase, pas de CouchDB !
Je reviendrai sur la différence entre les deux dans quelques lignes…

Avant de parler de Couchbase, Rags nous explique l’intérêt du NoSQL, comme la scalabilité horizontale et donc un coût maitrisé.
Si vous n’êtes pas encore convaincu, je vous suggère de voir leur document « Why NoSQL ? » sur le site de Couchbase : http://www.couchbase.com/why-nosql/nosql-database

Qu’est-ce que Couchbase ?

Maintenant se pose la grande question : qu’est-ce que Couchbase ?
La réponse courte : C’est une base NoSQL, orienté « clé/valeur » mais avec des fonctionnalités orientées « document ».

Avant Couchbase, il y avait Membase : un Memcached survitaminé, avec réplication des caches, persistances, une interface d’admin Web bien sympa, etc.
Membase n’existe plus, Couchbase le remplace et l’intègre.

On peut donc utiliser Couchbase comme un cache mémoire pure, en mode clé/valeur, avec l’API cliente de Memcached.

Mais alors, quel est le rapport entre Couchbase et CouchDB ?
Couchbase intègre CouchDB !
Pour prendre un exemple simple : quand vous sauvegardez un document JSON, ce dernier va être envoyé dans le cache Memcached, puis persisté sur disque avec le moteur de CouchDB (de manière asynchrone).

On peut comparer le processus avec celui de MongoDB, à la différence que MongoDB ne possède pas son module de Caching, mais délègue cette gestion à l’OS à l’aide de « Memory-mapped files », qui va flusher sur disque de temps en temps.

Pour mieux illustrer mes propos, voici l’écran de monitoring lorsque je fais une écriture massive :

On remarque un pique de « ops per second » : ce sont mes écritures massives
Mais la donnée se trouve seulement en mémoire, pas encore sur le disque !
On voit alors que le « disk write queue » augmente d’un coup : c’est la file d’attente avant persistance sur disque.
Quelques secondes plus tard, on voit que cette dernière diminue petit à petit.
En même temps, on voit que la base augmente sur le disque dans « couch disk/data size ».
Et toujours en même temps, on voit des « creates per sec » pendant cette phase de persistance.

Pour comprendre comment fonctionne Couchbase, et comment il intègre CouchDB, vous pouvez lire ceci : http://www.couchbase.com/couchdb

Comment lire/écrite dans Couchbase (en Java) ?

Couchbase est donc très orienté performance, puisqu’on va exploiter au mieux la mémoire.
De plus, les opérations de l’API sont de type « fire and forget » : quand on demande de sauvegarder un document, l’API nous rend la main tout de suite sans savoir si la sauvegarde a bien fonctionné !
Pour cela, il va y avoir un mécanisme semblable aux « Future » en Java, ou au « Write Concern » en MongoDB ou Cassandra.
L’API va donc nous renvoyer un objet « OperationFuture », sur lequel on va pouvoir, plus tard, demander d’attendre la fin de la sauvegarde.
Voici un exemple de code, plus parlant je l’espère :)

CouchbaseConnectionFactory factory = new CouchbaseConnectionFactory(
  Arrays.asList(URI.create("http://localhost:8091/pools")),
  "default",
  "");
client = new CouchbaseClient(factory);

GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();

// on stocke ici les futures des opérations d'insertion
List<OperationFuture<Boolean>> futures = new ArrayList<OperationFuture<Boolean>>();

for (int cpt = 0; cpt < 80000; cpt++) {

 // création d'un document
 People people = new People();
 people.setFirstname("Mathias");
 people.setLastname("Kluba");

 // j'utilise ici GSON pour construire mon JSON à partir de mon POJO
 String json = gson.toJson(people);

 // on va générer un ID pour chaque item
 UUID uuid = UUID.randomUUID();

 // sauvegarde de l'objet
 OperationFuture<Boolean> setResult = client.set(
 uuid.toString(),
 0,
 json);

 // on conserve le future pour plus tard...
 futures.add(setResult);
}

// maintenant qu'on a balancé les sauvegarde sur le server,
// on attend la fin de chacune d'entre elles,
// et on vérifie que la sauvegarde a réussie
for (OperationFuture<Boolean> f : futures) {
 Assertions.assertThat(f.get()).isTrue();
}

Ce qui est sympa, c’est que même la lecture peut être asynchrone : on balance plein de requête au serveur, et on demande ensuite le résultat en asynchrone à l’aide du Future.

Comme tout fonctionne en mode asynchrone, cela peut poser des problèmes lors de l’écriture : comment gérer les écritures concurrentes ?
En base relationnelle, on va vouloir faire une transaction, pour être sûr que personne ne modifie la même donnée.
Mais Couchbase ne supporte pas les transactions !
Ceci dit, il y a moyen de gérer la concurrence de manière « Optimiste » : on va admettre que tout va bien dans la plupart des cas, et s’il y a un problème de concurrence, alors on va gérer l’erreur au niveau de l’application.

Pour ça, il y a le mécanisme de CAS (Check and Set).
Le CAS va se servir d’une sorte de Checksum qui va nous permettre de vérifier si quelqu’un n’a pas modifié le même objet entre temps.
Ex :

  • Je récupère un objet avec son CAS
  • Je le modifie en mémoire
  • Quelqu’un d’autre le récupère aussi, le modifie et le sauvegarde en base
  • Lorsque je le sauvegarde, je précise la valeur du CAS que j’ai obtenu
    • ERROR : la valeur du CAS fournie n’est pas le même en base : quelqu’un a modifié le même objet entre temps : la sauvegarde échoue

Comme Linus Torvalds disait : TALK IS CHEAP. SHOW ME THE CODE !

GsonBuilder builder = new GsonBuilder();
Gson gson = builder.create();

// création d'un document
People people = new People();
people.setFirstname("Mathias");
people.setLastname("Kluba");

// j'utilise ici GSON pour construire mon JSON à partir de mon POJO
String json = gson.toJson(people);

// on va générer un ID pour chaque item
UUID uuid = UUID.randomUUID();

// sauvegarde de l'objet
OperationFuture<Boolean> future = client.set(
 uuid.toString(),
 0,
 json);

Assertions.assertThat(future.get()).isTrue();
System.out.println(future.getStatus());

// obtient l'objet avec son CAS
CASValue<Object> withCas = client.gets(uuid.toString());

people = gson.fromJson((String)withCas.getValue(), People.class);
people.setFirstname("Roi Mathias 1er");

json = gson.toJson(people);

// tente de mettre à jour l'objet avec son CAS
CASResponse response = client.cas(uuid.toString(), withCas.getCas(), people);

Assertions.assertThat(response).isEqualTo(CASResponse.OK);

// tente de mettre à jour une seconde fois:
// mais qui ne va pas marché car on n'a pas le bon CAS

response = client.cas(uuid.toString(), withCas.getCas(), people);

Assertions.assertThat(response).isEqualTo(CASResponse.EXISTS);

Un autre truc sympa, hérité de Memcached, c’est la notion de TTL (Time To Live).
Si je reprends le code de la sauvegarde, on remarque un 2 ème argument de type Integer :


// sauvegarde de l'objet
OperationFuture<Boolean> setResult = client.set(
 uuid.toString(),
 0,
 json);

C’est en fait le TTL en seconde : je peux ainsi dire « sauvegarde moi cette donnée, mais elle expire dans 60sec ».
Ici, cas particulier, je ne veux pas d’expiration, la valeur du TTL est donc 0.

Bon, faire un « set/get » en mode clé/valeur, c’est bien mais parfois ça peut être limitant…
Et si je veux maintenant requêter sur le « lastname » ?

C’est là que l’approche « orienté document » de Couchbase est intéressante :
Ils prennent pour postula que la majorité de vos requêtes seront de type « clé/valeur ».
Tout d’abord, quand vous sauvegardez une valeur au format JSON, cette valeur sera traitée de manière très spéciale par Couchbase.
Mais par défaut, on ne peut donc pas requêter sur les champs de votre JSON. Pour ce faire, il faut créer une sorte d’index sur les champs que vous avez besoin de requêter.

Je dis bien « une sorte d’index », car en Couchbase on appelle ça une « Vue » (comme les Vue SQL), mais le plus fun c’est qu’on code cette vue avec une fonction de Map-Reduce (ou Map seulement) ;)

En résumé : les Vues sont des « Map-Reduce » à la demande.

Quand on interroge une vue, on peut demander le résultat déjà calculé au préalable, ou re-indexer la base en re-exécutant la fonction de Map-Reduce.
Voici ce qui se passe si j’interroge une vue en forçant la re-indexation :

Voici un exemple de Vue et son utilisation en Java :


Query query = new Query();

query.setReduce(false);
query.setIncludeDocs(false);
query.setStale(Stale.FALSE);

View view = client.getView("people", "bylastname");

Paginator paginator = client.paginatedQuery(view, query, 500);

while (paginator.hasNext()) {
 ViewRow row = paginator.next();
 System.out.println(
  "ID: " + row.getId() +
  " Key: " + row.getKey()  +
  " Value: " + row.getValue());
}

Et voilà à quoi ça ressemble coté Couchbase :

Petit détail : si vous forcer la re-indexation, ça ne s’applique bien sûr qu’aux nouvelles données ou aux données changées… donc si vous avez déjà 80000 items, que le Map-Reduce est terminé, et que vous ajoutez 80 items, le Map-Reduce sera plus rapide :)

Pour conclure sur les aspects requêtages, Couchbase, offre une API Memcached (pour de la haute performance) mais offre aussi une API REST, beaucoup plus « Web friendly ».
Les applications Web peuvent alors récupérer le JSON avec une URL REST qui ressemble à cela :
http://localhost:8091/couchBase/default/00006e0b-4b58-465d-b045-e2a485baaa51

Pour obtenir le résultat d’une vue, cela ressemble à cela :
http://localhost:8091/couchBase/default/_design/dev_people/_view/bylastname?limit=10&skip=0

La présentation de Couchbase s’arrête à peu près là. On voit alors que Couchbase est orienté Web, Performance, Scalabilité Horizontale, et que son installation et utilisation est très simple.

Couchbase et Cluster

Mais je souhaite aller plus loin,  j’ai voulu savoir comment fonctionne les aspects « scalabilité horizontale » de Couchbase.
Premièrement : tout comme Cassandra, chaque nœud Couchbase fonctionnent de la même manière, aucun n’est spécialisé.

Couchbase utilise Memcached, donc exploite le même mécanisme de répartition de charge grâce au « Consistent Hashing » : les données sont uniformément réparties sur tous les nœuds disponibles.
Et si j’ajoute un nœud ?
Et bien il n’est pas exploité tout de suite : il est en attente de Re-Balancing.
Cette opération vous permet donc de re-balancer les données sur plusieurs nœuds à la fois, si vous en ajoutez plusieurs :

Attention : c’est une opération bloquante et elle peut prendre du temps, ce qui rend donc votre serveur indisponible. Il faut alors choisir le moment de son exécution judicieusement…. mais au moins, ce n’est pas automatique, et ça ne va pas planter votre prod lors d’une forte charge !

Le même mécanisme fonctionne quand on supprime un nœud :

Tout ça, c’est pour répartir la charge, et donc :

  • Exploiter le disque + mémoire de plusieurs machines pour accélérer les requêtes de lecture/écriture
  • Exploiter le CPU de plusieurs machines pour accélérer le Map-Reduce des Vues

Mais qu’en est-il de la résilience ?
Elle est assuré grâce à la réplication, et on configure ça au niveau d’un « Bucket ».

Un « Bucket » c’est l’équivalent d’une base en SQL.
Il n’y a pas de notion de « table » (ou « collection » comme en Mongo), si vous voulez séparer des données alors il faut créer un autre Bucket.
Ce dernier possède un quota en mémoire, et un nombre de réplica.
Si le nombre de réplica est 1, alors la donnée sera écrite sur 2 nœuds.

Conclusion

En conclusion, j’ai été agréablement surpris.
Premièrement, j’ai confondu Couchbase et CouchDB : Couchbase c’est un mixe des meilleurs choses de Memcached et CouchDB, c’est donc pas mal :)
L’approche « Vue avec Map-Reduce » est intéressante : on ne ralentit pas les écritures pour mettre à jour un index, ou construire un « b-tree », alors que finalement on va requêter sur seulement quelques champs du document…
Il y a donc de très bonnes idées, même si, à mon avis, il reste des choses à améliorer.
Couchbase est donc différent des autres solutions NoSQL, avec ses avantages et ses inconvénients.
Vous avez donc une base de données qui vient étoffer votre panel de solutions NoSQL, ce qui vous donne encore plus de choix, mais ce qui rend la tâche encore plus complexe :)

Java varargs – Pas de magie: soyez prudents!

J’ai rencontré une erreur bizarre, liée aux varargs, sur un projet que je visitais récemment…

Tout d’abord un petit rappel de ce que sont les varargs en java:
Comme vous le savez surement les varargs permettent de définir des méthodes qui peuvent accepter des arguments optionnels et au nombre variable. Définir une telle méthode est aussi simple que ci-dessous:

public void doSomething(String... values) {
// do something here
}

La méthode est alors accessible avec aucun paramètre ou n’importe quel nombre de paramètres:

public void callDoSomething() {
doSomething();
doSomething("a","b","c");
}

Bref les varargs apportent une vrai souplesse dans la définition et l’utilisation des méthodes aux paramètres optionnels et/ou variables en java.
Maintenant que ce passe-t-il réellement ? Comment JAVA gère cette magie ? En fait la réalité est relativement simple et peut-être pas aussi idéale que ce qu’on aurait pu le souhaiter… En effet les varargs ne sont pas une nouvelle super feature de java mais plutôt une feature de son compilateur. Le compilateur va tout simplement remplacer les appels en générant des signatures de méthodes basées sur le passage de tableaux d’objets. Si l’on reprends notre exemple précédent nous avons donc l’équivalent suivant:

public void doSomething(String[] values) {
// do something here
}

Et le client:

public void callDoSomething() {
doSomething(new String[0]);
doSomething(new String[]{"a", "b", "c"});
}

Bref les varargs c’est bien, ça simplifie l’écriture de mon code mais… Mais si je ne comprends pas comment ça marche je peux faire des erreurs bête… Et je dirais que même si je comprends comment ça marche je peux louper certains changements relativement facilement.

Prenons un exemple simple. Disons qu’une équipe A est chargée de développer un jar d’utilitaires pour une équipe B.

La première version de l’utilitaire contient dans une des classes la méthode suivante:

public void doSomething() {
// do something here
}

L’équipe B utilise ma méthode avec un appel simple:

public void callDoSomething() {
doSomething();
}

Dans la release suivante l’équipe A ajoute un nouveau paramètre optionnel à l’aide des VarArgs. L’intérêt est évident: les utilisateurs existants n’auront pas à modifier leur code pour continuer d’utiliser l’utilitaire, et les nouveaux utilisateurs peuvent bénéficier des nouvelles options offertes par le nouvel argument optionnel.

public void doSomething(String... params) {
// do something here
}

Mon utilitaire comprends d’autres méthodes et la nouvelle amélioration en plus d’ajouter cette modification améliore le comportement de plusieurs autres de ces outils (sans changement dans l’API). L’équipe B particulièrement intéressée par ces améliorations décide donc de migrer vers la nouvelle version.
Les développeurs des l’équipe B sont des gens consciencieux, ils ont développé leurs tests unitaires, d’intégration etc.. et leur intégration continue fonctionne bien. Sans aucune modification à leur code l’inclusion de ma nouvelle version n’a causé aucun soucis.

L’équipe B décide donc d’inclure mon nouveau jar dans leur environnement de production. Comme leur application est très lourde et n’a pas été modifiée (aucun changement de code) l’équipe décide pour faciliter la manoeuvre de ne mettre à jour que le jar utilitaire.

A l’execution comme vous vous en doutez puisque vous avez bien suivi la réalité des VarArgs l’erreur suivante se produit:

java.lang.NoSuchMethodError: fr.fastconnect.java.blog.varargs.MyProviderClass.doSomething()V
at fr.fastconnect.java.blog.varargs.UsingClass.main(UsingClass.java:10)

Note: dans le contexte de mon projet réel l’inclusion a heureusement été faite sur un environnement de test (je voulais donner un effet « scénaristique » un peu plus dramatique).

Gardez donc en tête, les Varargs ne sont pas une fonctionnalité de runtime mais de compilation, il n’y a pas de magie donc soyez prudents!!

Soirée Paris JUG – Les annotations Java

Le 14 décembre 2010 s’est tenu la  soirée du ParisJUG à l’ISEP portant sur les annotations Java. J’ai eu la chance d’y assister dans le cadre du sponsoring de FastConnect.

La soirée à été animé par Olivier Croisier, auteur du blog The Coder’s Breakfast.

La soirée s’est découpé en deux parties :

  • Le concept d’annotations.
  • Les possibilités offertes par les annotations.

Les annotations Java sont des méta-informations qui peuvent être utilisés dans les différentes phases du cycle de vie d’une applications, à savoir :

  • à la génération du code.
  • à l’exécution de l’application.

Les annotations Javadoc sont les ancêtres des annotations qui sont apparus avec Java 1.5.

/**
* Documentation sur une méthode.
* @param args Le permier argument de la méthode.
* @return Ce que retourne la méthode.
*/

Ce bout de code présent juste au dessus d’une méthode permet à l’outil Javadoc de générer la documentation de l’application.

Mais où trouve-t on des annotations ?

Tout ou presque est annotable en Java, du package à l’argument d’une méthode en passant par les classes. Olivier Croisier nous donne l’exemple suivant de l’utilisation de l’annotation @Deprecated fournit par Java 5 :


@Deprecated
public class Pojo {

@Deprecated
private int foo;

@Deprecated
public Pojo() {
@Deprecated
int localVar = 0;
}

@Deprecated
public int getFoo() {
return foo;
}
public void setFoo(@Deprecated int foo) {
this.foo = foo;
}
}

Les annotations sur les packages peuvent être renseigner dans un fichier package-info.java :


/**
 * La documentation sur ce package.
 */
@UneAnnotation
package org.fastconnect.unpackage;

La création d’une annotation.

Une annotation est une interface qui réponds à certaines spécificités, comme nous pouvons le voir dans l’exemple suivant :

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.TYPE})
public @interface Toaster {

 public static enum Level { LOW , MIDDLE , HARD }

 String toasterName() default "";

 Level level() default Level.MIDDLE;

}

Notre annotation « @Toaster » est définit donc une @interface. Elle est elle-même annotée par @Retention et @Target.

  • @Retention permet de définir la durée de vie de notre annotation dans le cycle de vie du code. Ici, nous décidons de la garder jusque dans le Runtime.
  • @Target permet de définir les cibles qui pourront porter notre annotations. Nous décidons ici de permettre aux attributs et aux classes d’être annotées par @Toaster.

Comme une interface, notre annotation peut comporter des attributs, comme nous pouvons le voir avec notre énumération Level.

La différence avec les interfaces réside dans les signature des méthodes. En effet, le mot clé default permet de définir une compile-time constant qui sera utilisé par défaut.

Les annotations, a quoi ca peut servir cette bête la ?

Il est possible d’utiliser les annotations à différents moment du cycle de vie de l’application. A l’image des métas données pour Javadoc, il est possible de se faire des annotations pour la réalisation de documentation. L’exemple d’une annotation @Todo est souvent utilisé dans ce sens.

Si les annotations sont conservées au Runtime, les mécanismes de réflexion Java permettent de récupérer les annotations ainsi que leur contenu. Il peut donc être possible de faire de la méta programmation à l’aide des annotations. Les annotations JPA ou des EJB 3 sont des bons exemples de méta programmation.

APT pour Annotation Processing Tool est un outil qui permet de processer les annotations lors de la compilation. Il est ainsi possible se servir des annotations pour étendre des règles de compilation ou de développer un générateur de code.

Conclusion

Comme nous avons pu le voire, les annotations offre de nombreuses possibilités, que ce soit lors de l’écriture du code. Cependant, il ne faut pas oublier que trop d’annotations tue les annotations.

Java Finalization is Bullshit

The purpose of this article is to speak about some poorly understood mechanisms of the Java Virtual Machine. If you already know all these mechanisms, you should contact our RH to work at FastConnect ;-)

So please, consider the following program. If we run it with a java heap limited to 8Mb (thanks to the option -Xmx8m), it will fail with an ‘OutOfMemoryException’. But it will happen some time after 1000 loops ; other times after only 100 loops. It seems random.

class Padding
{
	private byte[] padding;
	Padding() {
		this.padding = new byte[1024*1024]; //1Mbyte
	}
	protected void finalize() throws Throwable {
		this.padding[0] = this.padding[1];
	}
}

public class Main
{
	public static void main(String[] args) throws Exception {
		int cpt = 0;
		try {
			for(;; ++cpt) {
				new Padding();
			}
		} catch (OutOfMemoryError oom) {
			System.err.println("oom after "+cpt+" loops.");
			throw oom;
		}
	}
}

Here a little quiz. Can you explain this behavior? Is there a leak in the program? Why the number of iterations before the OOM is random?

Before reading the following, I suggest you try to run this little program by yourself. Maybe you will have to increase the size of the padding array to reproduce the described behaviour.

The first question is not very difficult. The random behavior is due to the garbage collector. To provide good performance, recent Java VMs manage the memory at the same time as your code is executed. That’s why if the Java heap is not big enough it is possible to have an OOM even if there is no leak, even if the marked objects are always smaller than the java heap…

That’s why you must never be stingy with memory. A java program needs to have a lot of free memory to maximise safe and efficient memory management.

The next questions are more tricky. If we remove the ‘finalize method’, the program works fine, even with a very small heap. Can you explain why?

class Padding
{
	private byte[] padding;
	Padding() {
		this.padding = new byte[1024*1024]; //1Mbyte
	}
}

public class Main
{
	public static void main(String[] args) throws Exception {
		for(;;) {
			new Padding();
		}
	}
}

Again, I suggest you try to run this program. If you open ‘jconsole’ or ‘jvisualvm’, you should see a very abnormal memory activity: the curve is flat (but few very rare accidents). So, I have two questions:

  • Why the curve of this very simple program is so flat?
  • Why the existence of a ‘finalize method’ disrupts the memory management.

As a java expert, I expect you already read (several times) the documentation about the java garbage collector, its tuning and its algorithms.

The default GC uses a generational algorithm where the allocation in Eden generation is a simple stack allocation. With our simple program, Padding objects are never promoted from the young generation to the tenured generation during the collection. It means that objects are fastly allocated in stack. At some point later the Copy GC is triggered and it copies nothing. In this particular – but not rare – situation the java memory management is more efficient than the C++ malloc because allocations and deallocations are held in stack and batched.

I hope my explanations were clear. If not, you should read the following article: Java theory and practice: Urban performance legends, revisited.

But we have not answered the last and most tricky question: why the existence of a ‘finalize method’ disrupts the memory management. A hint is in the title of this article. Please consider the following program:

class Padding
{
	static java.util.Set<Padding> retention = new java.util.HashSet<Padding>();
	private byte[] padding;
	private int id;
	Padding(int id) {
		this.id = id;
		this.padding = new byte[1024*1024]; //1Mbyte
	}
	protected void finalize() throws Throwable {
		System.err.println("finalize is called for Padding[id="+id+"]");
		retention.add(this);
	}
}

public class Main
{
	public static void main(String[] args) throws Exception {
		int cpt = 0;
		try {
			for(;; ++cpt) {
				new Padding(cpt);
			}
		} catch (OutOfMemoryError oom) {
			System.err.println("oom after "+cpt+" loops.");
			throw oom;
		}
	}
}

Again I suggest you test it several times with different heap size (-Xmx64m for example). Its behaviour is very stable: it fails all of the time at the same moment.

On blogs and forums, I often read “finalize is called when the object is reclaimed”. This is wrong! When a finalizable object is no more referenced, the JVM adds it to the JVM’s finalization queue. At some point later, the JVM’s finalizer thread will dequeue it, call its ‘finalize method’, and record that its finalizer has been called. It is only when the garbage collector rediscovers that the object is unreachable that it reclaims it.

This behaviour is a problem: it makes the code of the Garbage Collector very complex, it is error prone for the JVM providers and it consumes a lot of memory. It is the main reason why the Finalization is not in the Java specification for mobile phones and (credit) card.

Elsewhere there is no good usage of the Java Finalization. For example, if you close sockets thanks to a ‘finalize method’, you will have a big problem if you have a lot of free memory but not enough file descriptors. Garbage Collectors are good for managing the managed memory and only that.

You can refer to this article which explains how finalization works and how not to use it.

Cyril Martin (mcoolive).

Les fonctions récursives terminales

Il y a quelques temps déjà, je discutais avec un collègue dans ce lieu de socialisation qu’est la machine à café. Nous ne parlions pas de notre travail, mais nous parlions tout de même d’informatique. Nous eûmes l’échange suivant :

  • Les langages fonctionnels, c’est nul ; les fonctions récursives ça peut casser la pile d’exécution.
  • Bah non, si tu utilises une fonction récursive terminale…
  • Une quoi ?

Les mots ne sont pas exacts, mais je crois être fidèle à nos intentions. Depuis j’ai eu d’autres occasions de vérifier que cette notion de programmation était souvent très mal connue. D’où mon idée d’écrire un article sur le sujet.

Qu’est-ce qu’une fonction récursive terminale ?

Une fonction récursive terminale est une fonction qui lorsqu’elle fait appel à elle-même, le fait en tout dernier. Concrètement il n’y a pas de calcul entre l’appel récursif et l’instruction return. Le terme anglais est « tail-end recursion ».

Il s’agit donc d’une manière d’écrire votre fonction récursive.

À quoi ça sert ?

Cela sert à produire du code efficace, clair, et qui ne risque pas de faire déborder la pile d’exécution.

D’un point de vue algorithmique, une fonction récursive terminale est équivalente à une simple boucle. Écrire ainsi vos fonctions récursives permettra au compilateur d’optimiser efficacement le code produit.

Dans un langage impératif, le programmeur a souvent l’idée d’utiliser une boucle, donc la question est moins importante. En revanche, dans un langage fonctionnel comme OCaml ou parallelC#, qui vous invite par sa syntaxe et son esthétisme à user de fonctions, il est primordial d’écrire des fonctions terminales.

Un exemple commenté : la fonction ‘factorielle’ en langage C

Pour illustrer mon propos, j’ai choisi de commenter plusieurs implémentations de la fonction « factorielle », écrites en langage C.

La plupart des programmeurs (dé)formés par des années de pratique du C, préfèrent utiliser une boucle for :

int factorial(int n) {
    int accu=1;
    for(; n; --n) accu *= n;
    return accu;
}

Ce code fonctionne parfaitement bien. Il a l’unique défaut d’être finalement assez éloigné de la définition mathématique de la factorielle, c’est-à-dire que la plupart des gens le jugeront peu naturel.

Considérons maintenant une implémentation plus naturelle, très proche de la définition apprise sur les bancs d’école. C’est aussi la façon dont la plupart des programmeurs débutants écrivent cette fonction :

int factorial (int n) {
    if (n == 0) return 1;
    else return n * factorial(n - 1);
}

Cette implémentation récursive est assurément très mauvaise. Elle consomme beaucoup de mémoire en pile, donc c’est lent et ça peut aussi se terminer prématurément si on l’appelle avec un paramètre trop grand (stack overflow). La version récursive terminale pallie précisément ce problème :

int factorial (int n, int accu) {
    if (n == 0) return accu;
    else return factorial(n - 1, n * accu);
}

C’est une tail-end recursion car l’appel récursif est le dernier statement. Vous avez certainement remarqué que pour ce faire, j’ai dû introduire un paramètre supplémentaire ; ceci est discuté plus loin.

Comment ça marche ?

Comme vous le savez certainement, lorsque l’on effectue un appel de fonction, la fonction appelante stocke sur la pile d’exécution les paramètres de la fonction appelée et l’adresse de retour. La fonction appelée utilise ses paramètres pour effectuer ses calculs et, à la toute fin, saute à l’adresse précédemment sauvegardée. On revient alors dans le code de la fonction appelante qui dépile avant de continuer…

Sachant cela, il est facile de comprendre qu’un grand nombre d’appels de fonctions imbriqués fait grossir la pile, et que les fonctions récursives représentent un risque important de débordement de pile (stack overflow).

L’optimisation opérée par le compilateur consiste à produire un code qui, lors de l’appel récursif, au lieu d’empiler… et de dépiler plus tard, va remplacer les paramètres actuels et simplement boucler. Ceci est possible dans le cas particulier d’une fonction récursive terminale car il n’y a plus rien à faire dans la fonction appelante.

En fait cette optimisation n’est pas propre aux fonctions récursives. Toute fonction se terminant par un appel de fonction (appel récursif ou pas) est susceptible de profiter de la même optimisation. On parle de sibling call ou tail call.

Un dessin valant mieux que des mots, voici un exemple d’optimisation de tail call sur une machine windows x86 : la méthode foo se termine par un appel à la méthode bar, donc on libère la place réservée pour l’exécution de foo avant d’exécuter bar.

 As soon as foo          While foo        When foo is about to
   is called            is executing        tail call to bar

                      |              |                     
                      +--------------+                     
                      |              |                     
                      |              |                     
                      | foo's frame  |                     
                      |              |      |              |
                      |              |      +--------------+
|              |      |              |      |return address|
+--------------+      +--------------+      +--------------+
|return address|      |return address|      |              |
+--------------+      +--------------+      |              |
|              |      |              |      |              |
|  stack args  |      |  stack args  |      |  stack args  |
|    of foo    |      |    of foo    |      |    of bar    |
|              |      |              |      |              |
+--------------+      +--------------+      +--------------+
|              |      |              |      |              |
|              |      |              |      |              |
|              |      |              |      |              |
|   Frame of   |      |   Frame of   |      |   Frame of   |
| foo's caller |      | foo's caller |      | foo's caller |
|              |      |              |      |              |
|              |      |              |      |              |

Ce dessin suggère un algorithme assez grossier pour libérer la pile de foo. En fait le compilateur peut faire quelque chose de très subtile grâce à un calcul de dépendances des variables et expressions. On produit alors un code en tout point semblable à la meilleure des boucles (un décompilateur ne pourra pas faire la différence).

Pour ce faire, le compilateur doit effectuer des analyses compliquées. Ces analyses sont biens connues : factorisation, élimination du code mort, une allocation optimales des registres, l’analyse d’échappement, le calcul de causalité (élimination des synchronisations inutiles, invalidation des caches mémoires), et bien d’autres. Cependant elles font toujours l’objet d’une recherche intense. Le défi est d’être capable d’effectuer une optimisation de qualité dans un temps raisonnable (utilisable dans un JIT) et/ou en consommant très peu de mémoire (carte à puce, téléphone). Mais je me perds dans mes digressions, revenons à notre sujet principal.

A-t-on raison de parler d’optimisation ?

Certains auteurs contestent, à tort, le terme d’« optimisation ». L’argument avancé est qu’un programme qui se termine par une erreur stack overflow n’a visiblement pas le même comportement qu’un programme qui se termine en retournant un résultat (ou qui ne se termine pas).

En effet, pour être correcte une optimisation ne doit pas modifier la sémantique d’un programme (et ce n’est pas toujours simple, cf. la gestion de la sécurité, les interactions avec le ramasse miettes, la gestion des exceptions et des signaux etc.) Mais en algèbre de compilation on considère toujours que la machine cible possède une mémoire infinie. Sans cela, la plupart des optimisations ne pourraient pas être effectuées par le compilateur au prétexte que cela pourrait prévenir une erreur au runtime.

Voici un exemple pour les réfractaires :

a = 1 + 1 + 1 + 1; // ça fait 4

La plupart des compilateurs effectuent les calculs à la compilation quand c’est possible. Pourtant, ce faisant on change effectivement le comportement du programme à l’exécution car il est possible que l’évaluation de cette expression provoque un stack overflow dans certaines conditions.

Donc oui c’est une optimisation ; le terme consacré est Tail Call Optimization (TCO).

Que faire du paramètre supplémentaire ?

Comme je l’ai déjà fait remarquer dans l’exemple de la factorielle, pour écrire une fonction récursive sous forme terminale, il est souvent nécessaire de modifier sa signature en y ajoutant un paramètre.

Que faire de ce paramètre ?

  • On peut le considérer comme une astuce qu’il convient de cacher. Dans notre exemple, le paramètre accu joue le même rôle que la variable de boucle res.
  • On peut aussi le considérer comme un paramètre qui fait sens et qui améliore notre fonction. Ainsi dans notre exemple, le second paramètre accu sert à choisir la valeur du cas de base (factorielle(0)).

En général, on souhaite masquer ce paramètre en trop. On définit alors deux fonctions : la première que l’on cache et qui prend deux paramètres, la seconde qui appelle la première et expose la signature voulue. Pour faire cela, il existe diverses méthodes selon le langage (et les paradigmes) utilisé.

On peut définir la fonction récursive dans la fermeture (closure) de la fonction principale. C’est une technique couramment utilisée dans les langages fonctionnels.

(* Exemple en OCaml *)
let factorielle = function n ->
    let rec __factorielle = function n -> function accu ->
        if n = 0
        then accu
        else __factorielle (n-1) (n*accu)
    in __factorielle n 1
;;

Plus classique, on peut restreindre la portée de la fonction récursive. Ainsi en C, on peut utiliser le mot static pour empêcher une fonction d’être exposée dans l’interface du module.

// Exemple en C
static int __factorielle (int n, int accu) {
    if (n == 0) return accu;
    else return __factorielle(n-1, n*accu);
}
int factorielle (int n) {
    return __factorielle(n, 1);
}

La plupart des langages objets offrent des modificateurs d’accès. En Java, on utilisera les modificateurs public et private :

/* Exemple en Java */
public Mathematiques {
    private static __factorielle(int n, int accu) {
        if (n == 0) return accu;
        else return __factorielle(n-1, n*accu);
    }
    public static factorielle (int n) {
        return __factorielle(n, 1);
    }
}

Certains langages, tels que Python ou C#, permettent de donner une valeur par défaut à un paramètre. Ceci est particulièrement utile dans notre cas.

# Exemple en Python
def factorielle(n, accu=1):
    if n == 0: return accu
    else: return factorielle(n-1, n*accu)

Évidemment il n’y a pas une seule bonne manière de faire. Vous devrez juger quelle est la technique la plus pertinente, la mieux adaptée à votre cas. C# est sans doute le langage qui offre le plus de possibilités.

Est-ce que toutes les fonctions récursives peuvent être écrites sous une forme terminale ?

Oui, toute. Les preuves : les théories de la calculabilité, la logique combinatoire, combinateur de point fixe.

Un second exemple commenté : les nombres de Fibonacci

Considérons ensemble une fonction un peu plus difficile à écrire : la fonction qui calcule les nombres de Fibonacci.

Pour écrire la fonction de Fibonacci sous forme récursive terminale, nous avons besoin d’utiliser non pas un, mais deux paramètres supplémentaires (accu0 et accu1). Ceci est à rapprocher du fait que l’implémentation itérative utilise deux variables de boucle (res0 et res1) et du fait que la définition récursive donne deux cas de base (fibonacci(0) et fibonacci(1)).

# Pour changer, voici du Ruby
# Nombre de Fibonacci, implémentation naïve
def fibonacci_naive(n)
    if n == 0
        0
    elsif n == 1
        1
    else
        fibonacci_naive(n-1) + fibonacci_naive(n-2)
    end
end

# Nombre de Fibonacci, implémentation avec une boucle
def fibonacci_boucle(n)
    res0, res1 = 1, 0 # Pour n'avoir qu'un seul test, on commence au rang -1
    (1..n).each do res0, res1 = res1, res0+res1 end
    res1
end

# Nombre de Fibonacci, implémentation récursive terminal
def fibonacci_recursive(n, res0=1, res1=0)
    if n == 0
        res1
    else
        fibonacci_recursive(n-1, res1, res0+res1)
    end
end

La fonction de fibonacci est particulière à bien des égards. L’implémentation naïve, outre le fait de ne pas être terminale, présente le défaut de nous faire calculer de nombreuses fois les mêmes valeurs (par exemple Fibonacci(4) = Fibonacci(3) + Fibonacci(2) et Fibonacci(3) = Fibonacci(2) + Fibonacci(1), donc nous calculons deux fois Fibonacci(2)…)

Ici, l’implémentation récursive terminale apporte beaucoup plus qu’une bonne gestion de la pile d’exécution. C’est souvent le cas. Ceci ne signifie pas qu’il faille systématiquement utiliser des fonctions récursives terminales (par exemple, je trouve plus lisible d’utiliser une syntaxe for each pour parcourir une collection). Mais je crois que l’étude des fonctions terminales est très formatrice, et qu’elle aide les développeurs à concevoir des algorithmes concis et optimaux.

Est-ce que ça marche pour tous les langages ?

Malheureusement non !

En fait, d’un point de vue technique, cela dépend non pas du langage mais du compilateur. Cependant, on peut remarquer que certains langages, tels que F# et OCaml, définissent ce comportement dans leur spécification.

Pour les langages fonctionnels, ne pas optimiser correctement les appels terminaux est généralement considéré comme un bogue. C’est pleinement justifié car ces langages promeuvent l’utilisation de fonctions récursives. En revanche, la chose est plus discutée pour les autres langages. D’ailleurs, nous allons voir que les plateformes Java et .NET ne sont pas très satisfaisantes sur ce point.

Faisons ensemble un petit tour d’horizon parmi les technologies les plus utilisées.

Langage C

La norme C ne dit rien. Mais tous les bons compilateurs (VS, gcc) proposent une option pour effectuer cette optimisation. Voici un extrait de l’aide de gcc, le fameux compilateur C :

-foptimize-sibling-calls
Optimize sibling and tail recursive calls. Enabled at levels -O2, -O3, -Os.

Notons au passage que le choix des options de compilation est vraiment très important. Beaucoup de programmeurs ont l’habitude de compiler le programme de release avec l’option « -O2 » et de déboguer avec les options « -O0 -g ». Ce faisant on change le comportement du programme et il est possible de provoquer une erreur stack overflow qui n’était pas l’objet des tests.

Plateforme Java

Java pour les nuls

Une des techniques essentielles de Java est l’utilisation d’un langage intermédiaire appelé bytecode Java. Un programmes Java est transformé (traduit, compilé) par le JDK en bytecode Java qui pourra être exécuté (interprété ou compilé à la volée et exécuté) par toutes les JVMs.

Un bytecode est un langage neutre, facile à parser et à projeter sur les architectures courantes. Le bytecode Java est un langage à pile exposant certaines abstractions caractéristique du paradigme objet tels que les appels de méthodes virtuelles et statiques.

L’optimisations des tail calls en Java

Le JDK peut optimiser les fonctions récursives terminales en les transformant en simple boucle. Mais il est beaucoup plus intéressant d’optimiser tous les appels de méthodes terminaux (tail calls) car c’est un cas plus général qui traite aussi la récursivité croisée et la programmation par continuation. Mais le JDK ne peut pas optimiser les tail calls en général car le bytecode Java n’offre pas les instructions nécessaires (on ne manipule pas directement la pile et on ne peut pas faire de jump n’importe où). Ceci ne peut donc être fait que par la JVM lors du chargement des classes.

Dites moi ce dont vous avez besoin et je vous expliquerai comment vous en passer

The Java Language Specification ne dit rien au sujet des tail recursive calls.

Chaque vendeur de Java VM fait ce qu’il veut. La JVM fournie par Sun, qui est de fait l’implémentation de référence, n’effectue pas cette transformation au grand damne des faiseurs de langages dynamiques fondés sur Java (script Groovy, JRuby, etc.)

public class Main {
    public static void main(String[] args) { main(args); }
}

Si vous exécutez le programme ci-dessus sur une JVM de Sun, il se terminera toujours rapidement et lamentablement par une exception Stack Overflow. Heureusement certaines implémentations de Java gère les appels de méthodes terminaux, c’est le cas notamment de la JVM d’IBM.

Sun ne communique pas beaucoup autour de ce sujet. On peut tout de même voir que ce problème est noté en very very very low priority (Votez pour ce bug !) Tant que que Sun/Oracle ne supportera pas officiellement les tail recursive calls, je crains qu’il ne nous faille renoncer à ce type de programmation en Java.

Voici quelques billets très intéressants sur le sujet : java closures controversy, non java languages on jvm et tail calls in the VM.

Plateforme .NET

Pour les mêmes raisons qu’en Java, l’optimisation des tail recursive calls peut être effectuée aussi bien par le compilateur que par la plateforme d’exécution (CLR). En revanche, l’optimisation des tail calls ne peut être effectuée que par le CLR, au moment du chargement des bibliothèques.

L’optimisation des tail calls étant requis pour traiter correctement la récursivité croisée et, plus généralement, les continuations, choses couramment utilisée dans les langages fonctionnels, la plateforme .NET qui se veut être une plateforme multi-langages et multi-paradigmes, doit optimiser correctement les tail calls.

Voici ce que dit la documentation de CIL (le langage intermédiaire commun utilisé par .NET) :

CIL ECMA 335 spec “use a tail. prefix to specify that the current stack frame should be released before transferring control. If the call would transfer control to a method of higher trust than the original method the stack frame will not be released.”

Malheureusement, le comportement de la plateforme .NET de Microsoft est loin d’être satisfaisant. Pour des raisons qui me sont obscures (ordre des paramètres sur la pile, compatibilité binaire avec du code natif, concurrence avec le GC), la plate-forme .NET ne sait pas, en général, optimiser correctement les tail calls.

Voici une liste des conditions requises pour que le JIT du CLR2 effectue une TCO :
http://blogs.msdn.com/davbr/pages/tail-call-jit-conditions.aspx. J’ai été étonné par certaines de ces limitations qui me semblent mal fondées. Microsoft s’est visiblement montré très défensif dans les deux premières versions du CLR.

Concrêtement les comportements du compilateur et de la VM dépendent de l’architecture de la machine. L’implémentation de la CLR pour x86, au lieu d’une savante manipulation, utilise une fonction magique et affreusement lente qui décale le sommet de pile (cf. le dessin au début de l’article). Par conséquent, la plupart des compilateurs .NET ne produisent jamais le préfixe tail. et les compilateurs de langages fonctionnels tels que F# ou parallelC# transforment les fonctions récursives terminales en boucles. Mais il s’agit plus d’un workaround qu’autre chose. En effet cela ne traite pas les cas de co-récursivités et plus généralement de la correcte optimisation d’un programme écrit par continuation.

L’implémentation pour x64 est meilleure. En générale, tous les arguments d’une fonction peuvent être stockés dans les registres processeurs car ils sont beaucoup plus nombreux sur cette architecture. Grâce à cela, les performances des tail calls sont acceptables. Par conséquent, sur x64, le CLR effectue des TCO même quand le prefix tail. n’a pas été spécifié dans le code CIL, si les conditions requises sont réunies…

Microsoft a bien conscience du problème. Un effort significatif a été apporté à l’optimisation des tail calls dans la CLR4 sur x86_64. Maintenant les optimisations faciles sont toujours effectives et, pour les rares cas restant, on utilise la fonction magique dont je parlais tantôt afin de toujours tenir compte du prefix .tail dans le code IL.

Oyez, programmeur. F# fonctionne correctement (enfin) sur CLR4/x86_64 !

Remarquez que l’optimisation correcte des tail calls a été l’occasion pour Microsoft d’améliorer son compilateur en supprimant certaines actions inutiles (copie de buffer de values, etc.) Je me répète sûrement : la récursivité aide les développeurs, mêmes les très bons, à concevoir des algorithmes concis et optimaux.

Python

Guido van Rossum, le créateur et leader du langage de programmation Python a publié un billet horrible où il tente d’expliquer pourquoi les TCO sont le mal et ne devraient surtout jamais être effectuées en Python.

Certains des arguments avancés sont tout simplement faux. Vous trouverez quelques arguments bien construits dans les commentaires de cet autre billet.

Python est un langage qui a été pensé pour être facile à utiliser, et non pas pour produire des programmes rapides. En cela je comprends parfaitement que les TCO ne soient pas une priorité, mais il n’y a aucune raison valable pour s’y opposer par principe.

Ruby

La spécification de Ruby ne dit rien au sujet des tail recursive calls.

Yukihiro Matsumoto (le créateur de Ruby) semble être favorable au TCO, mais, il n’a pas souhaité forcer toutes les implémentations de Ruby à le supporter. Voilà une position qui me semble sage ;-).

YARV, qui est l’interpréteur Ruby officielle, supporte le TCO depuis la version 1.9. Notons que c’est très récent et que l’option, considérée encore instable, est désactivée par défaut (il est nécessaire de commenter une ligne et de recompiler l’interpréteur).

Quant à JRuby et Ruby.NET, ils dépendent évidemment des capacités de la plateforme sous-jacentes..

Ceci signifie que vos programmes Ruby ne devraient pas se fonder sur le TCO, car dans ce cas ils ne seront pas portables sur toutes les Ruby VM.

Java Performance – tips

In my day to day work at FastConnect I am often dealing with java performance that is mandatory in many of our projects. While digging into my code and java language I found some tips that helped us to perform better. I decided to share some of them with you in this article.

Note that these tips are various, some of them are quite extreme and can create a code complication that is not always welcome. Thus they must be used carefully and only when it is really required. Some other on the other way can also help to reach as well as performance an easier readability. Note that as always a good documentation and structure is necessary to make the overall comprehensible.

Limit Object creations

One aspect that can be easily implemented and that can help in frequently called scenarios is to limit the creation of Objects. As you know creating an Object requires first the JVM to allocate space for it and perform all the linking operations and second to manage this object in the garbage collection process, these two operations can have performance impact when dealing with frequent short-lived object creation.See the following example:

package fr.fastconnect.java.performance;

public class ObjectCreationOverheadDemo {
	/* Static fields and methods */
	/**
	 * 10.000.000
	 */
	private final static int ITERATIONS=10000000;

	public static void main(String[] args) {
		// launch a first test with both and no mesurement to not take
		// JVM initialisations in the measurement
		executeWithNewObject();
		executeWithExistingObject();
		// perform test and measure performance
		long start = System.currentTimeMillis();
		executeWithNewObject();
		long duration = System.currentTimeMillis()-start;
		System.out.println("Duration with new object creation : " + duration);
		start = System.currentTimeMillis();
		executeWithExistingObject();
		duration = System.currentTimeMillis()-start;
		System.out.println("Duration with single object       : " + duration);
	}

	private static void executeWithNewObject() {
		for(int i=0;i<ITERATIONS;i++) {
			ObjectCreationOverheadDemo demoObject = new ObjectCreationOverheadDemo();
			demoObject.intValue = i;
			demoObject.longValue = i;
			/*.. Application usage of the object ..*/
		}
	}

	private static void executeWithExistingObject() {
		ObjectCreationOverheadDemo demoObject = new ObjectCreationOverheadDemo();
		for(int i=0;i<ITERATIONS;i++) {
			demoObject.intValue = i;
			demoObject.longValue = i;
			/*.. Application usage of the object ..*/
		}
	}

	/*
	 * Object fields
	 */
	private int intValue;
	private long longValue;
}

Running the test on my machine I got the following output that confirm the impact of object creation.

Duration with new object creation : 174
Duration with single object : 26

Note that in this situation this is small objects and if the impact exist it is in fact very small. Indeed a few millisecond for so many iterations may seems limited. However think on the impact with bigger objects, I’m thinking on the impact on garbage collection mainly.

Working with String:

Java String methods are very fast, however a bad usage of String features can have a very heavy impact on your program performance.

String concatenation

When you are performing multiple string concatenations, try to use a StringBuilder instead of a ‘+’ between the two strings. StringBuilder class is the ultimate optimized solution to append a string to another.

package fr.fastconnect.java.performance;

public class StringConcatenationDemo {
	/* Static fields and methods */
	/**
	 * 10.000
	 */
	private final static int ITERATIONS=10000;
	private final static String strToAppend = "Str To Append";

	public static void main(String[] args) {
		// launch a first test with both and no mesurement
		// to not take JVM initialisations in the measurement
		executeStringPlus();
		executeStringBuilder();
		// perform test and measure performance
		long start = System.currentTimeMillis();
		String strPlusResult = executeStringPlus();
		long duration = System.currentTimeMillis()-start;
		System.out.println("Duration with '+' concatenation : " + duration);
		start = System.currentTimeMillis();
		String strBuilderResult = executeStringBuilder();
		duration = System.currentTimeMillis()-start;
		System.out.println("Duration with StringBuilder     : " + duration);
		System.out.println("String are the same : "+strPlusResult.equals(strBuilderResult));
	}

	private static String executeStringPlus() {
		String resultString="";
		for(int i=0;i<ITERATIONS;i++) {
			resultString += strToAppend;
		}
		return resultString;
	}

	private static String executeStringBuilder() {
		StringBuilder strBuilder = new StringBuilder();
		for(int i=0;i<ITERATIONS;i++) {
			strBuilder.append(strToAppend);
		}
		return strBuilder.toString();
	}
}

Running this example on my machine I got the following output:
Duration with ‘+’ concatenation : 7685
Duration with StringBuilder : 7
String are the same : true

Here again the benefit impact of StringBuilder is clear.

getBytes vs custom implementation

getBytes method in java is quite slow. This is related mainly to char encoding parameters and validations. In certain circonstances where encoding is the same on every machine and doesn’t cause issues, we can use an optimised custom implementation:

package fr.fastconnect.java.performance;

import java.util.Arrays;

public class StringGetBytesDemo {
	/* Static fields and methods */
	/**
	 * 10.000
	 */
	private final static int ITERATIONS=100000;
	private final static String str = "String to get bytes from";

	public static void main(String[] args) {
		// launch a first test with both and no mesurement
		// to not take JVM initialisations in the measurement
		execute();
		executeCustom();
		// perform test and measure performance
		long start = System.currentTimeMillis();
		byte[] bytes = execute();
		long duration = System.currentTimeMillis()-start;
		System.out.println("Duration with getBytes : " + duration);
		start = System.currentTimeMillis();
		byte[] bytesCustom = executeCustom();
		duration = System.currentTimeMillis()-start;
		System.out.println("Duration with custom impl     : " + duration);
		System.out.println("byte arrays are the same : "+Arrays.equals(bytes, bytesCustom));
	}

	private static byte[] execute() {
		for(int i=0;i<ITERATIONS-1;i++) {
			str.getBytes();
		}
		return str.getBytes();
	}

	private static byte[] executeCustom() {
		for(int i=0;i<ITERATIONS-1;i++) {
			char buffer[] = new char[str.length()];
			int length = str.length();
			str.getChars(0, length, buffer, 0);
			byte b[] = new byte[length];
			for (int j = 0; j < length; j++) {
				b[j] = (byte) buffer[j];
			}
		}
		char buffer[] = new char[str.length()];
		int length = str.length();
		str.getChars(0, length, buffer, 0);
		byte b[] = new byte[length];
		for (int j = 0; j < length; j++) {
			b[j] = (byte) buffer[j];
		}
		return b;
	}
}

Note that in many cases the java getBytes and custom method will just produce the exact same byte array. This is something you must check in your tests (providing a switch to use one or the other can also be a good idea).
Duration with getBytes : 74
Duration with custom impl : 15
byte arrays are the same : true

Serialization tips

In many situations serialization can be costly as well in term of size and as well performance. This is linked as IO operations are related to size of data, and serialization’s goal is as you know to be able to send data on an IO channel.
In such situation when performance is a must it may be useful to use a custom serialization.

Use Externalizable

To enable custom serialization for your object, you must implements the Externalizable interface. This interface will provide you 2 methods writeExternal and readExternal that will allow you to deal by yourself with the object serialization.
When using the native java serialisation on your objects, java will therefore use your Externalizable method instead of native serialization process.
Be careful to use this only if the custom serialization has better performance or footprint than the java classic serialization. This is mostly the case on objects that have complex fields and not only primitive.

Create byte array on your own

In some really extreme situations you may want to go even deeper in the performance optimization and create by yourself a byte array that you will use for writing to a network or file stream. (Or basically any stream). Indeed classic java serialization save some class meta-datas. All this overhead can be avoided by creating a custom byte-array. Using custom byte array creation allow also to perform custom batching etc. Basically this is really powerful but has also an impact on code readability. Thus such implementation must be used carefully, be well documented and must use methods with explicit names.

Try to size your data to avoid buffer resizing

When java developer want to create a byte array (serializing data dealing with various buffer…), the most common used class is the ByteArrayOutputStream. this object allow you to write bytes to a stream and to store it in a byte array. The big point with it is that the goal of it is too be able to resize this array anytime if you keep putting data in it. However as explained before the ByteArrayOutputStream can be costly because of ArrayCopy. If you are able to size the data correctly, or if you can send the data by chunk from a defined size, I encourage you to use the ByteBuffer class that will perform a lot better! It will bring however the constraint of not resizing the byte array.

When working directly with more deep layer native networking etc., it is even possible to create the ByteBuffer in the native heap! This will be even more faster to just put data in it as well as to send to network layer but you won’t be able to get any backing byte array back in you java application!! Also this kind of buffer use Native memory and will not be controlled by the heap so be even more careful regarding memory leaks ;)

Multithreading:

Avoid thread common variables and synchronization of resources

When you can do it try to avoid sharing any ‘thread common variables’. When you use a buffer for example try to create one per thread, even if you have to merge them at the end. To do so you can ether use ThreadLocal variables or even better just use full different instances (when you use spring remember that if singleton is the default object scope, using prototypes can be sometimes a better approach). Even if this sounds basic, keep it in mind: achieving a performing multithreaded implementation can be done only with the very minimum synchronization points.

Queue instead of synchronize

When your process can be done asyncronously and you have a synchronization point then queue your process (java.util.concurrent.BlockingQueue) and use a single consumer. This will allow that you don’t block all other thread because of this synchronization point and can help to make your application perform better. If you are afraid that the queue can grow too much don’t worry: BlockingQueue can limit the size of the queue. When this size will be reached then you will pay for the synchronization… Anyway in many situation this can help ;)

Use optimized concurrent classes

Use the java concurrent package that contains many Collections and tools that contains high performance implementations for concurrency scenario. This package contains lot of very useful classes that will perform a lot better than other implementations or custom basic synchronized.

Links

http://java.sun.com/developer/technicalArticles/Programming/Performance/

This list is of course not exhaustive and I invite each of you to comment the article and add your tips ;)

How to dump JVM IBM heap and open it for analysis in MAT

Most of the clients I’m working with are using SUN JVM. However some of them are using IBM VM. As you know, even if the same code can be launched on both many things vary from the one to another, VM options, Garbage collection tuning etc…

I worked recently on solving a memory leak issue and I’ve been confronted to the following straightforward problem: How to be able to get a heap dump that I can analyse with the tools I know well, and that I consider as the best in Open-Source tools. For dump analysis I’m referring to MAT (Memory Analyser Tool from SAP hosted by Eclipse foundation).

First Step: How to get a dump

The best way to get a dump for analysis with IBM VM is in fact not to get a « heap dump » but a « system dump ». This is a particularity from IBM VM. Basically a heap dump will not allow you to access fields names and values in the analysis step (using MAT). This means that you will not be able to benefits from the power of OQL queries.

In order to get a dump use the following options:

-Xdump:system:events=user,request=exclusive+prepwalk

You can now export a system dump by launching a kill signal to the application (kill -3 under linux/unix).

I also encourage you to use dumping on OutOfMemory error that is very useful in most of scenario!

-Xdump:system:events=systhrow,filter=java/lang/OutOfMemoryError,request=exclusive+prepwalk

Note that the -Xdump is very powerfull and you can really customize the option to launch a dump on almost any event in your application. Read more here http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=/com.ibm.java.doc.diagnostics.60/html/dumpagents_syntax.html.
System dumps will produce a file named core.(date).(time).(process id).dmp

This file will not be usable as it is for analysis, you must first transform it using the IBM JVM jextract command on it. This will generate a zip file that you’ll be able to process.

Open the dump in MAT for analysis

Now you can import the zip file generated from the first step into MAT and analyse your dump!

I hope this article will help you if you work with IBM VM and solve memory issues some day.

Soirée DDD avec Eric Evans

Les followers de @parisjug étaient au courant, Eric Evans (auteur de domain driven design) était de passage à Paris en ce début de semaine. Grâce à leur réactivité, les membres du Jug ont pu nous organiser une petite présentation ce lundi. (Merci au passage à l’Epita qui a accueilli l’évènement).

Malgré l’annonce tardive, la blogosphère Java à bien réagi et l’amphi était plein, environ 110 personnes avaient fait le déplacement.

La présentation d’Eric s’articulait sur les contextes d’utilisation du DDD ainsi que ses apports en comparaison aux techniques plus classiques.

Pour résumer en quelques lignes : Pour les applications dont la complexité inhérente est dûe à la complexité du domaine, l’approche DDD permet, en recentrant les développeurs sur le métier, de collaborer avec des experts du domaine afin de construire un logiciel flexible répondant à leur problématique.

On retiendra donc en points clés de cette présentation :

  • L’unicité du langage : les développeurs et les experts métiers doivent être capables d’exprimer les mêmes concepts avec le même vocabulaire. Ceci afin de faciliter la communication et de permettre le partage de connaissances. Cette unicité doit se retrouver aussi bien dans les phases de design que dans l’implémentation.
  • Le besoin pour les équipes de développement de travailler en étroite collaboration avec les experts du domaine (ce qui permettra justement la construction de ce langage commun et l’échange d’information)
  • Le développement itératif, encore une fois, le besoin de communication entre les équipes de dev et le métier nécessite un processus itératif. Seules les itérations permettent de capitaliser sur les connaissances acquises précédemment.
  • Un contexte d’application clair et nettement défini. La nécessité d’un langage unique et d’un modèle adapté aux problématiques métiers exclu de facto les possibilités d’universalité de ce modèle. Un modèle n’étant donc qu’une représentation partielle adaptée aux besoins, il faudra définir les clairement les limites d’application de ce modèle afin de pouvoir s’en affranchir dans d’autres contextes.

La soirée s’est conclue par la traditionnelle troisième mi-temps, délocalisée cette fois à la porte d’Italie !

Prochain évènement de l’agenda : Soirée ALT.NET

Présentation Data Grid au Paris JUG

Demain soir à 19h30 se déroule le traditionnel Paris JUG, événement réunissant la communauté Java parisienne autour d’un certain nombre de sujets d’actualité. Je précise pour les non initiés ;-)
Il est vrai que d’ordinaire nous ne postons jamais au sujet de cet événement car d’autres confrères le font très bien.
Cependant, il me semblait important de mentionner la présentation Data Grid qui me semble très intéressante et d’actualité dans un certain nombre de projets.

Qu’est ce que le Data Grid ?

Tous ceux qui sont confrontés à des problématiques de temps d’accès aux données ou de traitements massifs de ces données recherchent des solutions alternatives aux bases de données relationnelles.
Les Data Grid en mémoire permettent de clusteriser la mémoire d’un parc hétérogène de machines tout en fournissant un accès simple (clef valeur, ?QL, template d’objet) et transactionnel, à cet espace mémoire virtualisé.
Le stockage des données en mémoire est fiabilisé par un certain nombre de mécanismes et le Data Grid fournit les indispensables mécanismes d’intégration au SI dont, notamment, les bases de données existantes.
Lorsqu’on revient aux fondamentaux; j’aime considérer les technologies en identifiant leur impact sur les éléments matériels sous-jacents (CPU, mémoire, disque, réseau); on se rend compte que les technologies Data Grid fournissent une réponse très adaptée dans beaucoup de situations car les temps d’accès et la bande passante de la mémoire sont infiniment supérieurs à ce que peuvent proposer les disques.
Évidemment, on peut toujours payer très cher pour essayer d’atteindre les mêmes performances que la mémoire avec des disques spécialisés, mais le veut on ?
Oui me diront certains, ça rassure ! Mais c’est un autre débat et puis ces temps-ci, en période de crise, ça fait pas très classe ;-)

La base de données change de rôle

En tant que partenaire GigaSpaces, un des leaders de ce domaine, nous voyons de plus en plus d’entreprises de toute taille qui démarrent des études afin de choisir le Data Grid qui permettra à leurs projets stratégiques de gérer les problématiques de stockage haute-performance et de traitements de données.
Depuis un peu plus de 3 ans environ, une grosse partie de notre activité a été de concevoir avec nos clients, et mettre en production, des systèmes stratégiques fondés sur les technologies Data Grid. Non seulement ça marche, mais les performances sont au rendez vous :
– plusieurs dizaine de milliers d’appels par seconde
– des temps de réponse inférieurs à la milliseconde sur des réseaux classiques 1GB
tout çà sur une « lame » d’entrée de gamme à quelques milliers d’euros pièce.

Lorsqu’on voit la prolifération d’offres alternatives aux bases de données relationnelles, il est désormais clair que le rôle de la base de données est en train de changer (ou a déjà changé pour certains) dans le système d’informations.
La base de données a imposé son hégémonie dans nos systèmes d’information depuis des années et a fait la richesse de certains acteurs du secteur, mais il est désormais acquis que la base de données ne peut pas monter en charge sans faire la richesse des vendeurs de matériel.
D’ailleurs, curieusement, dans certains cas, ce sont les mêmes ;-)

Le Data Grid s’est donc imposé avec le temps; projet après projet, secteur d’activité après secteur d’activité comme la solution alternative à la base de données, notamment lorsque celle ci se trouve sur le chemin critique des transactions.
Des pionniers de ce secteur comme GigaSpaces martèlent ce message depuis une dizaine d’années maintenant.
D’autres utilisateurs et fournisseurs d’infrastructures massivement distribuées ont utilisé un certain nombre de concepts  d’informatique distribuée sous-jacents aux technologies Data Grid dans leurs infrastructures internes.
Je pense notamment à Google, Amazon, Facebook qui n’ont pas eu le choix, pour des raisons économiques que de créer des alternatives pour stocker et exploiter de manière efficace leurs données.
Comme il faut rendre à César ce qui appartient à César on peut même dire que ces acteurs incontournables du Web poussent à leurs limites les technologies traditionnelles et dans certains cas poussent (ou tirent comme vous voulez) l’innovation. Le concept MapReduce a été popularisé par Google par exemple.

Le Data Grid, la boite à outils pour le traitement massif de données

Lorsqu’il m’arrive de présenter GigaSpaces, je fais souvent l’analogie entre GigaSpaces et les concepts sur lesquels s’appuient ces énormes infrastructures en indiquant que GigaSpaces peut être vu comme une technologie permettant de productiser, et rendre accessible de manière simple un ensemble de concepts d’informatique distribuée.
Cela nous permet à nous, architectes et développeurs, d’avoir les outils permettant d’assurer la performance et la scalabilité de nos applications distribuées.
D’ailleurs à ce propos, je voudrais mentionner cet article qui explique bien un certain nombre de principes de l’informatique distribuée : http://www.hfadeel.com/Blog/Files/ArtofDistributed.pdf tels que les concepts de Grid Computing, de Master Worker ou de MapReduce.

Au delà de la gestion des données en mémoire, de manière fiabilisée et très performante, le Data Grid permet de paralléliser les traitements sur les données en permettant l’implémentation de patterns tels que Master Worker ou Map Reduce.

Les capacités du Data Grid sont multiples, les technologies Data Grid sont matures (car d’autres ont débuggé pour vous, vous savez ce que c’est ;-) ), et les cas d’utilisation foisonnent, tous secteur d’activités confondus.

Voilà, j’espère que je vous aurai donné envie de venir assister à cette présentation Data Grid si vous n’étiez déjà convaincus, et je serai présent ainsi qu’un certain nombre de nos consultants (dont Jean-Michel qui participe à cette présentation) pour discuter de tout çà avec vous.

Pour venir : http://www.jugevents.org/jugevents/event/16041
Les détails de la présentation : http://www.parisjug.org/xwiki/bin/view/Meeting/20090512