[JAVA] Soyez conscient du garbage collection et évitez les fuites de mémoire

Pour éviter les fuites de mémoire, j'ai répertorié autant que je sache quand le ramasse-miettes fonctionne et quand il ne fonctionne pas.

À propos du ramasse-miettes

Une fonction qui détruit automatiquement un objet à la discrétion de Java lorsqu'il n'est plus géré par aucune variable. La "méthode finalize ()" détruit l'objet, mais en Java, même si cette méthode est définie, le timing ne peut pas être géré.

Cas où le garbage collection fonctionne

Depuis que je l'ai confirmé avec un processus simple, j'ai réduit la zone de tas pour faciliter les fuites de mémoire. (Comme une fuite de mémoire à 2M) Taille initiale du tas-Xms1m Taille maximale du tas-Xmx2m

① Lorsque toutes les variables faisant référence à l'objet deviennent nulles

Puisque "assigner null à une variable = la variable ne représente aucun objet", cela fonctionne lorsque toutes les variables représentant un certain objet deviennent nulles. Substitute null = C'est comme dire "Je ne l'utilise plus", donc Garbage Collection le jugera inutile.

public class AddList01 {
	public static void main(String[] args) {
		//Processus 1
		List<String> list1 = new ArrayList<>();
		addList(list1);
		list1 = null;
		//Processus 2
		List<String> list2 = new ArrayList<>();
		addList(list2);
		list2 = null;
	}

	private static void addList(List<String> list) {
		for (int i = 0; i < 100000; i++) {
			list.add("a");
		}
	}
}

② Lorsque l'objet n'est plus manipulé

Le garbage collection fonctionne lorsque la variable qui gère l'objet n'est plus répertoriée. "J'ai vu toute la source, mais elle n'est plus gérée, donc c'est normal de jeter le contenu."

public class AddList02 {
	public static void main(String[] args) {
		List<String> list1 = new ArrayList<>();
		List<String> list2 = new ArrayList<>();

		//Processus 1
		for (int i = 0; i < 100000; i++) {
			list1.add("a");
		}

		//Processus 2
		for (int i = 0; i < 100000; i++) {
			list2.add("b");
		}
	}
}

③ Variables locales dans les boucles et try-catch

Le garbage collection fonctionne lorsqu'un objet terminé dans une boucle, tel que déclaré dans une boucle ou instancié, quitte la boucle. En effet, les variables déclarées à l'intérieur de la boucle ne peuvent pas être gérées en dehors de la boucle.

public class AddList03 {
	public static void main(String[] args) {
		try {
			//Processus 1
			for (int i = 0; i < 10; i++) {
				List<String> list1 = new ArrayList<>();
				for (int j = 0; j < 100000; j++) {
					list1.add("a");
				}
			}

			//Processus 2
			List<String> list2 = new ArrayList<>();
			for (int i = 0; i < 10; i++) {
				list2 = new ArrayList<>();
				for (int j = 0; j < 100000; j++) {
					list2.add("b");
				}
			}

			//Processus 3
			int i = 0;
			while (i < 10) {
				List<String> list3 = new ArrayList<>();
				for (int j = 0; j < 100000; j++) {
					list3.add("c");
				}
				i++;
			}

			//Processus 4
			List<String> list4 = new ArrayList<>();
			for (int j = 0; j < 100000; j++) {
				list4.add("d");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		//Processus 5
		List<String> list5 = new ArrayList<>();
		for (int j = 0; j < 100000; j++) {
			list5.add("e");
		}
	}
}

Au fait, si la variable "list2" est gérée après la boucle, il ne reste que la dernière instance de boucle.

public class AddList031 {
	public static void main(String[] args) {
		List<String> list2 = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			list2 = new ArrayList<>();
			for (int j = 0; j < 50000; j++) {
				list2.add("b");
			}
		}
		System.out.println(String.join("", list2));
	}
}

④ Variable locale de méthode

C'est le même principe que la boucle de ③. Cependant, la valeur référencée par l'argument et la valeur de retour restent.

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

	private static void addList() {
		List<String> list = new ArrayList<>();
		for (int j = 0; j < 100000; j++) {
			list.add("a");
		}
	}
}

Cas où le garbage collection ne fonctionne pas

(1) Maintenez l'instance dans la méthode principale et faites référence à l'objet dans une autre méthode.

Dans le cas (2) qui fonctionne, l'objet qui n'est plus géré a été ignoré, mais si vous faites référence à l'objet avec une autre méthode, le garbage collection ne fonctionnera pas. Pourquoi est-ce?

public class AddList05 {
	public static void main(String[] args) {
		//Processus 1
		List<String> list1 = new ArrayList<>();
		addList(list1);

		//Processus 2
		List<String> list2 = new ArrayList<>();
		addList(list2);
	}

	private static void addList(List<String> list) {
		for (int i = 0; i < 100000; i++) {
			list.add("a");
		}
	}
}

(2) Une instance de la liste est générée dans le champ

Puisqu'il est utilisé à divers endroits de la classe, c'est un cas où il est décrit dans le champ afin qu'il n'y ait pas beaucoup d'arguments. Il semble que la portée de la liste soit trop large pour décider que le garbage collection est inutile.

public class AddList06 {
	private List<String> list1 = new ArrayList<>();
	private List<String> list2 = new ArrayList<>();

	public static void main(String[] args) {
		AddList06 addList06 = new AddList06();

		//Processus 1
		for (int i = 0; i < 100000; i++) {
			addList06.list1.add("a");
		}

		//Processus 2
		for (int i = 0; i < 100000; i++) {
			addList06.list2.add("a");
		}
	}
}

③ Ce qui a été reçu comme valeur de retour de la méthode

Même si la méthode principale ne crée pas d'instance, celle qui est créée par la méthode appelée et reçue comme valeur de retour reste.

public class AddList07 {
	public static void main(String[] args) {
		//Processus 1
		List<String> list = addList();

		//Processus 2
		list = addList();

		//Processus 3
		list = addList();
	}

	private static List<String> addList() {
		List<String> list = new ArrayList<>();
		for (int i = 0; i < 50000; i++) {
			list.add("a");
		}
		return list;
	}
}

④ Au lieu de remplacer null, j'essaie de ne pas l'utiliser d'une autre manière.

Même si vous utilisez la méthode list pour supprimer des valeurs qui ne sont plus utilisées dans la liste que vous utilisez, l'objet semble rester référencé car il n'est pas nul.

public class AddList08 {
	public static void main(String[] args) {
		//Processus 1
		List<String> list1 = new ArrayList<>();
		addList(list1);
		list1.clear();

		//Processus 2
		List<String> list2 = new ArrayList<>();
		addList(list2);
		for (int i = 0; i < list2.size(); i++) {
			list2.remove(i);
		}

		//Processus 3
		List<String> list3 = new ArrayList<>();
		addList(list3);
	}

	private static void addList(List<String> list) {
		for (int i = 0; i < 50000; i++) {
			list.add("a");
		}
	}
}

Résumé

Vous pourriez penser que ce n'est pas si simple, mais pour le moment ・ "Autant que possible, divisez le traitement en petites parties par méthode et ayez-le localement" ・ "Utilisez là où vous pouvez réutiliser sans créer de variables et d'instances en vain" ・ "Veuillez ne pas déclarer de variables dans le champ" J'ai ressenti cela.

Après cela, s'il y a un traitement tel que l'acquisition SQL et l'enregistrement à partir de DB, sera-t-il divisé en fonction du but du traitement? Cela peut prendre du temps s'il y a plusieurs SQL, mais je pensais que c'était mieux que le ralentissement du traitement.

Recommended Posts

Soyez conscient du garbage collection et évitez les fuites de mémoire
Être conscient du code facile à lire
Être conscient de l'état interne (immuabilité) des objets et des effets secondaires des méthodes
[Java] Contenu de l'interface de collection et de l'interface de liste
5 choses dont les nouveaux programmeurs devraient être conscients
Points à prendre en compte lors de l'écriture de Java
[Java] Attention aux courts-circuits (évaluation des courts-circuits)
Différence entre le membre et la collection de rails routes.rb
Le servlet Java doit être conscient de l'environnement multithread