Quelle est la classe dangereuse?
Java a été initialement conçu comme un environnement contrôlé sûr. Malgré cela, Java Hotspot comprend toujours une "porte arrière" qui fournit des opérations de bas niveau qui peuvent manipuler directement la mémoire et les threads. Cette classe de porte dérobée - Sun.Misc.unsafe - est largement utilisée dans ses propres packages par JDK, comme Java.nio et Java.util.concurrent. Cependant, cette porte dérobée n'est pas du tout recommandée dans les environnements de production. Parce que cette API est très dangereuse, pas légère et instable. Cette classe non sécurisée fournit une vue de la structure interne du hotspot JVM et peut être modifiée. Parfois, il peut être utilisé pour apprendre la structure interne de la machine virtuelle sans débogage C ++, et parfois il peut être utilisé comme outils de surveillance et de développement des performances.
introduction
Récemment, je regardais le code source des forfaits concurrents Java et j'ai découvert la classe magique dangereuse. Je l'ai étudié attentivement et l'ai partagé avec vous ici.
La classe dangereuse est sous le package Sun.Misc et n'appartient pas à la norme Java. Cependant, de nombreuses bibliothèques de classe Java de base, y compris certaines bibliothèques de développement haute performance largement utilisées, sont développées sur la base d'une classe dangereuse, telle que Netty, Cassandra, Hadoop, Kafka, etc. La classe dangereuse joue un grand rôle dans l'amélioration de l'efficacité de l'opération Java et l'amélioration des capacités de fonctionnement sous-jacentes de la langue Java.
La classe dangereuse donne à Java la possibilité de faire fonctionner l'espace mémoire comme des pointeurs dans le langage C, et apporte également des problèmes de pointeur. L'utilisation excessive de la classe dangereuse augmentera les risques d'erreurs, donc Java ne le recommande pas pour être utilisé, et il n'y a presque pas de documentation officielle. Oracle prévoit de supprimer la classe dangereuse de Java 9, et si tel est le cas, ce serait trop grand.
Habituellement, il est préférable de ne pas utiliser la classe dangereuse, sauf si elle a un objectif clair et a également une compréhension approfondie de celle-ci. Pour utiliser la classe dangereuse, vous devez utiliser des méthodes délicates. La classe dangereuse utilise un modèle singleton et doit être obtenue via une méthode statique getunsafe (). Cependant, la classe dangereuse l'a restreinte. S'il s'agit d'un appel normal, il lèvera une exception de sécurité de sécurité; Seule la classe chargée par le chargeur de classe principale peut appeler cette méthode. Le code source est le suivant:
public static dangetaSaSafe () {class var0 = réflexion.getCallerClass (); if (! vm.IssystemDomainLoader (var0.getClassloader ())) {Throw New SecurityException ("Usare"); } else {return theunsafe; }}Il existe également des moyens de charger le code utilisateur à l'aide du chargeur de classe principal, tel que la définition du paramètre BootClassPath. Mais le moyen plus simple est d'utiliser la réflexion Java, comme suit:
Field f = unsetafe.class.getDeclaredField ("TheunSafe"); f.setAccessible (true); Dangereux dangereux = (dangereux) f.get (null);Après avoir obtenu l'instance dangereuse, nous pouvons faire ce que nous voulons. La classe dangereuse fournit les fonctions suivantes:
1. Gestion de la mémoire. Y compris l'allocation de la mémoire, la libération de la mémoire, etc.
Cette pièce comprend l'allocatememmemory (allocatememory), ReallocateMemory (ReallocateMemory), Copymemory (mémoire de copie), FreeMemory (mémoire libre), GetAddress (Adresse de mémoire de Getget), AdresseSize, PageSize, GetInt (obtenir l'intégralité pointé de l'adresse de mémoire), GetIntVolatile (obtenir l'inte Sémantique), Pount (Écrivez l'entier à l'adresse mémoire spécifiée), Pountvolatile (écrivez l'entier à l'adresse mémoire spécifiée et prend en charge la sémantique volatile), putOrDreDInt (écrivez l'entier à l'adresse mémoire spécifiée, commandés ou retardés). Getxxx et putxxx contiennent divers types d'opérations de base.
À l'aide de la méthode CopyMemory, nous pouvons implémenter une méthode de copie d'objet générale sans implémenter la méthode de clone pour chaque objet. Bien sûr, cette méthode générale ne peut atteindre une copie peu profonde de l'objet.
2. Instanciation d'objet non conventionnel.
La méthode AllocationInstance () fournit un autre moyen de créer une instance. Habituellement, nous pouvons instancier des objets avec une nouvelle ou une réflexion. Utilisez la méthode AllocationInstance () pour générer directement des instances d'objet sans appeler les constructeurs et autres méthodes d'initialisation.
Ceci est utile lors de la désérialisation des objets, permettant de reconstruire et de définir les champs finaux sans appeler des constructeurs.
3. Classes de fonctionnement, objets et variables.
Cette partie comprend StaticFieldOffset (décalage de domaine statique), DefinClass (classe de définition), DefianonyMoSClass (définition de classe anonyme), assureclassInitialialized (assurez-vous l'initialisation de la classe), objectFieldOffset (décalage du domaine d'objet) et d'autres méthodes.
Grâce à ces méthodes, nous pouvons obtenir le pointeur de l'objet. En compensant le pointeur, nous pouvons non seulement modifier directement les données pointées par le pointeur (même si elles sont privées), mais nous pouvons même trouver des objets que le JVM a déjà envisagé des ordures et peut être recyclé.
4. Fonctionnement du tableau.
Cette pièce comprend ArrayBaseOffset (obtient l'adresse de décalage du premier élément du tableau), ArrayIndexScale (obtient l'adresse d'incrément de l'élément dans le tableau), etc. ArrayBaseOffset est utilisé en conjonction avec ArrayIndexScale, et vous pouvez localiser la position de chaque élément dans le tableau en mémoire.
Étant donné que la valeur maximale du tableau de Java est Integer.max_value, la méthode d'allocation de mémoire de la classe dangereuse peut être utilisée pour implémenter des tableaux super grands. En fait, ces données peuvent être considérées comme un tableau C, vous devez donc faire attention à la libération de la mémoire au bon moment.
5. Synchronisation multi-thread. Y compris le mécanisme de verrouillage, le fonctionnement CAS, etc.
Cette partie comprend le surveillant, le trymonitorenter, le monitorexit, le comparaison de comparaison, la compareAndWap et d'autres méthodes.
Parmi eux, le surveillant, le trymonitorenter et le monitorexit ont été marqués comme obsolètes et ne sont pas recommandés.
Le fonctionnement CAS de la classe dangereux peut être le plus utilisé et fournit une nouvelle solution pour le mécanisme de verrouillage de Java. Par exemple, AtomicInteger et d'autres classes sont tous implémentés via cette méthode. La méthode ComparandSwap est atomique, qui peut éviter les mécanismes de verrouillage intenses et améliorer l'efficacité du code. Il s'agit d'un verrou optimiste, qui est généralement estimé que dans la plupart des cas, il n'y a pas de condition de course, et si l'opération échoue, elle continuera de réessayer jusqu'à ce qu'elle réussit.
6. suspendre et restaurer.
Cette partie comprend le parc, les unis et autres méthodes.
Suspendre un fil à travers la méthode du parc. Après avoir appelé le parc, le fil se bloque jusqu'à ce que le délai d'attente ou l'interruption se produise. Unsem peut terminer un fil en attente pour le restaurer à la normale. L'opération de suspension sur les threads dans l'ensemble du cadre de concurrence est encapsulée dans la classe Locksupport. Il existe différentes versions des méthodes de pack dans la classe LockSupport, mais en fin de compte, la méthode disApe.Park () est appelée.
7. barrière de mémoire.
Cette pièce comprend la charge, le magasin, FullFence et autres méthodes. Ceci est nouvellement introduit dans Java 8 pour définir les obstacles à la mémoire pour éviter la réorganisation du code.
LoadFence () signifie que toutes les opérations de charge avant la méthode sont terminées avant la barrière de mémoire. De même, Storefence () signifie que toutes les opérations de magasin avant cette méthode sont terminées avant la barrière de mémoire. FullFence () signifie que toutes les opérations de chargement et de stockage avant la fin de la méthode avant la barrière de mémoire.
Résumer
Ce qui précède est l'intégralité du contenu de cet article. J'espère que le contenu de cet article a une certaine valeur de référence pour l'étude ou le travail de chacun. Si vous avez des questions, vous pouvez laisser un message pour communiquer. Merci pour votre soutien à wulin.com.