Java Pooling

Kyu

Staff
Membre du personnel
Inscrit
4 Octobre 2009
Messages
327
Reactions
8
#1
Introduction

Dans les programmes où les actions sont rémanentes, nous avons besoin de gérer la logique plus intelligemment pour optimiser les performances.
Une de ces optimisations est de réduire le nombre de cycles cpu en réduisant le nombre d'instanciations d'objets.
En effet, la création d'objets est un processus coûteux.
Pour ce faire, nous pouvons utiliser des Pools qui contiendront les objets que nous avons créés auparavant et dont nous n'avons plus utilité.

Garbage Collector ou ramasse-miettes

En java, vous ne libérer pas la mémoire directement par vous même. Pour ce faire, la JVM (Java Virtual Machine) appelle le garbage collector (ramasse-miettes).
Il est appelé tout les X secondes et vérifie récursivement l'arbre d'objet pour obtenir les objets qui n'ont plus de références à un objet vivant (qui est référencé dans un thread).
Ainsi donc, quand le GC est appelé, les objets inutilisés seront libérés de la mémoire.

Comment implémenter le Pooling

L'idée du pooling est de réutiliser les objets inutilisés au lieu de les libérer et d'en recréer de nouveaux.
L'implémentation est assez simple et souple à mettre en place.

Tout d'abord, pour définir quels objets vont pouvoir être réutilisés, nous allons créer une interface Poolable.

Java:
public interface Poolable
{
 
    public void free();
}
Après ça, nous avons besoin d'une classe globale qui gérera nos objets réutilisables. Elle est composée d'une collection de type Map permettant de lier un type d'objet au Pool le gérant.
La classe Pool est statique car elle ne sert que de passerelle entre les Pools.
Java:
public class Pools
{
 
    private static Map<Class<?>, PoolType<? extends Poolable>> pools = new HashMap<>();
    
    public static <T extends Poolable> T obtain(Class<T> type)
    {
	PoolType<?> _pool = pools.get(type);
	if(pools.get(type) == null)
	{
	    _pool = new PoolType<T>();
	    pools.put(type, _pool);
	}
	    
	return _pool.obtain(type);
    }
    
    public static void free(Object object)
    {
	pools.get(object.getClass()).free(object);
    }
}
Comme pour une classe singleton, quand obtain(Class) est appelé, une recherche de la classe est effectuée dans la collection de Pools pour savoir si ce type de Pool est déjà enregistré. Si la recherche échoue, un type de Pool (PoolType) est instancié et enregistré dans la collection.
Java:
public class PoolType<T extends Poolable>
{
 
    private LinkedList<T> stack = new LinkedList<>();
    
    public <T> T obtain(Class<T> type)
    {
	Object _object = null;
	
	try {
	    _object = stack.getFirst();
	    stack.removeFirst();
	    System.out.println("Reuse object of type " + type.getSimpleName());
	} catch (NoSuchElementException e)
	{
	    try
	    {
		_object = type.newInstance();
		System.out.println("Instanciate new object of type " + type.getSimpleName());
	    } catch (InstantiationException | IllegalAccessException e1)
	    {
		e1.printStackTrace();
	    }
	}
	
	return (T) _object;
    }
    
    public void free(Object object)
    {
	T _object = (T) object;
	
	_object.free();
	stack.add(_object);
    }
}
Maintenant, la classe PoolType. Elle contient tout les objets d'un certain type où T est le type d'objet à stocker.
En utilisant <T extends Poolable> sur la classe, nous la définissons en tant que classe générique qui peux être utiliser pour plusieurs type d'objet héritant de Poolable.
La méthode obtain(Class) permet de récupérer un objet réutilisable dans la file d'attente ou en instancie un nouveau par réfection si la file d’attente est vide. Quand un objet doit être recyclé, il suffit d'appeler la méthode free(Object) qui va réinitialiser les valeurs de l'objet à celles par défauts et le mettre dans les fille d'attente des objets réutilisables.

Test

Pour tester tout ça, nous allons créer une nouvelle classe qui servira d'objet réutilisable.
Java:
public class PoolableObject implements Poolable
{
 
    private int value;
    private String valueStr;
    
    public PoolableObject()
    {
	
    }
    
    @Override
    public void free()
    {
	value = 0;
	valueStr = null;
    }
    
    public int getValue()
    {
        return value;
    }
 
    public void setValue(int value)
    {
        this.value = value;
    }
    
    public String getValueStr()
    {
        return valueStr;
    }
    
    public void setValueStr(String valueStr)
    {
        this.valueStr = valueStr;
    }
}
Ici, notre classe réécrit la méthode free() de l'interface Poolable permettant de définir comment l'objet réutilisable doit être réinitialisé.

Lancez ce code
Java:
public class Main
{
 
    public static void main(String[] args)
    {
	for(int i = 0; i < 5; i++)
	{
	    PoolableObject _poolObject = Pools.obtain(PoolableObject.class);
	    Pools.free(_poolObject);
	}
    }
}
La sortie console :

  • Instanciate new object of type PoolableObject
    Reuse object of type PoolableObject
    Reuse object of type PoolableObject
    Reuse object of type PoolableObject
    Reuse object of type PoolableObject

Conclusion

Cette méthode est très utile mais doit être manié avec précautions.

Avantages :

  • - Performances améliorés

Inconvénients :

  • - Augmentation de la mémoire utilisé
    - Fuite de mémoire si les objets ne sont pas libérés
    - Erreurs si les objets ne sont pas réinitialisés correctement
 
Dernière édition par un modérateur:

Geraff

Administrateur
Membre du personnel
Inscrit
13 Mars 2008
Messages
564
Reactions
0
#2
Merci pour le tuto.

Chose à ne pas oublié. Les méthodes et concepts de programmations sont comme les différents langages de programmation. Théoriquement on peut appliquer tout partout, dans la pratique il faut bien définir le contexte pour pouvoir choisir comme il se doit un langage ou une méthode.

On peut imaginer ici que si on développe sur un mobile ou un raspberry ou tout autre architecture limité en puissance de calcul il peut être envisageable de faire ce genre de solution. Sur un ordinateur la question se pose.
De plus même si vous êtes sur un environnement réduit il se peut que cette solution ne soit pas pour vous. Peut être que vous n'utilisez pas tant que ça la création d'objet éphémère.

Veillez à bien définir le goulot d'étranglement de votre application avant d'utiliser des solutions à des problèmes que vous n'avez pas ou n'aurez pas.
 

4R7Y

Contributeur
Inscrit
6 Mars 2011
Messages
213
Reactions
0
#3
Super interessant, je viens de voir ça.

Merci jones !
 

Kyu

Staff
Membre du personnel
Inscrit
4 Octobre 2009
Messages
327
Reactions
8
#4
A noter que c'est utile si vous voulez réduire l'impacte du garbage collector.
 
Haut Bas