2010-03-22 11 views
10

Je suis conscient que le but des variables volatiles dans Java est que les écritures à de telles variables sont immédiatement visibles aux autres threads. Je suis également conscient que l'un des effets d'un bloc synchronisé est de vider la mémoire locale du thread dans la mémoire globale.Que signifie le rinçage de la mémoire locale dans la mémoire globale?

Je n'ai jamais entièrement compris les références à la mémoire 'thread-local' dans ce contexte. Je comprends que les données qui existent seulement sur la pile sont locales, mais quand on parle d'objets sur le tas, ma compréhension devient floue.

J'espérais que pour obtenir des commentaires sur les points suivants:

  1. Lors de l'exécution sur une machine avec plusieurs processeurs, ne chasse la mémoire de thread local faisant simplement référence au rinçage du cache du processeur dans la RAM? Lors de l'exécution sur un ordinateur monoprocesseur, cela signifie-t-il quelque chose?

  2. S'il est possible que le tas possède la même variable à deux emplacements de mémoire différents (chacun étant accédé par un thread différent), dans quelles circonstances cela se produirait-il? Quelles implications cela a-t-il sur la collecte des ordures? Avec quelle agressivité les machines virtuelles font-elles ce genre de chose?

  3. (EDIT: ajout de la question 4) Quelles données sont vidées lors de la sortie d'un bloc synchronisé? Est-ce tout ce que le thread a localement? Est-ce seulement les écritures qui ont été faites à l'intérieur du bloc synchronisé?

    Object x = goGetXFromHeap(); // x.f is 1 here  
    Object y = goGetYFromHeap(); // y.f is 11 here 
    Object z = goGetZFromHead(); // z.f is 111 here 
    
    y.f = 12; 
    
    synchronized(x) 
    { 
        x.f = 2; 
        z.f = 112; 
    } 
    
    // will only x be flushed on exit of the block? 
    // will the update to y get flushed? 
    // will the update to z get flushed? 
    

Dans l'ensemble, je pense que je essaie de comprendre si des moyens de fil locale mémoire qui est physiquement accessible par une seule unité centrale de traitement ou en cas de partage de tas de fil local logique fait par la machine virtuelle? Tous les liens vers des présentations ou de la documentation seraient extrêmement utiles.

J'ai passé du temps à faire des recherches sur ce sujet, et même si j'ai trouvé beaucoup de littérature intéressante, je n'ai pas été en mesure de satisfaire ma curiosité concernant les différentes définitions de la mémoire locale du fil.

Merci beaucoup.

Répondre

6

La couleur dont vous parlez est connue sous le nom de «barrière de mémoire». Cela signifie que le CPU s'assure que ce qu'il voit de la RAM est également visible depuis d'autres CPU/cœurs. Cela implique deux choses:

  • Le compilateur JIT vide les registres de la CPU. Normalement, le code peut conserver une copie de certaines données globalement visibles (par exemple le contenu du champ d'instance) dans les registres du processeur. Les registres ne peuvent pas être vus des autres threads. Ainsi, la moitié du travail de synchronized est de s'assurer qu'aucun tel cache n'est maintenu. L'implémentation synchronized exécute également une barrière de mémoire pour s'assurer que toutes les modifications apportées à la RAM du coeur actuel sont propagées à la RAM principale (ou qu'au moins tous les autres coeurs sont conscients que ce noyau a les dernières valeurs - les protocoles de cohérence de cache peuvent être assez complexes).

La deuxième tâche est triviale sur les systèmes monoprocesseur (je veux dire, des systèmes avec une seule unité centrale de traitement qui a pour seul noyau), mais les systèmes monoprocesseur ont tendance à devenir plus rare de nos jours. En ce qui concerne les tas locaux, cela peut théoriquement être fait, mais cela n'en vaut habituellement pas la peine car rien ne dit quelles parties de la mémoire doivent être vidées d'un synchronized. Ceci est une limitation du modèle threads-with-shared-memory: toute la mémoire est censée être partagée. Au premier rencontré synchronized, la machine virtuelle Java doit ensuite vider tous ses "objets de segment de thread local" dans la RAM principale. Cependant, une JVM récente de Sun peut effectuer une «analyse d'échappement» dans laquelle une JVM réussit à prouver que certaines instances ne deviennent jamais visibles à partir d'autres threads. Ceci est typique de, par exemple, StringBuilder instances créées par javac pour gérer la concaténation de chaînes. Si l'instance n'est jamais passée en paramètre à d'autres méthodes, elle ne devient pas "globalement visible". Cela le rend éligible pour une allocation de tas locale-thread, ou même, dans les bonnes circonstances, pour l'allocation par pile. Notez que dans cette situation, il n'y a pas de duplication; l'instance n'est pas "à deux endroits en même temps". C'est seulement que la JVM peut garder l'instance dans un endroit privé qui n'entraîne pas le coût d'une barrière de mémoire.

+0

Merci pour les commentaires. Je suis en fait familier avec l'analyse d'échappement et cela a commencé ma confusion avec «thread-local». Je voudrais poser deux questions de suivi s'il vous plaît: 1. Si le compilateur a prouvé qu'un objet est thread-local, et que l'objet existe dans une région thread-locale du tas, pourquoi écrire dans cet objet à l'intérieur un bloc synchronisé doit être vidé? Le vidage du cache du processeur à la région du tas local-thread ne serait jamais observable par le thread qui a fait l'écriture? Est-ce que cela implique que les threads commutent les processeurs et commencent à s'exécuter avec un cache CPU différent? –

+0

2. Est-il possible qu'une JVM ait un objet existant simultanément dans deux emplacements de mémoire distincts sur le tas? Si oui, dans quelles circonstances cela se produirait-il? –

+0

1. Un 'synchronized' implique un flush de" tout ". Le 'synchronized' a un paramètre qui est l'instance sur laquelle le verrou est pris, mais le modèle de mémoire de Java exige que toute la vue de mémoire du fil soit soumise à la barrière de mémoire. Maintenant, si la JVM peut prouver qu'elle n'a pas besoin de vider un objet car aucun autre thread ne peut le voir (et que les objets non échappés sont de bons candidats), la JVM est libre de ne pas vider, selon la règle "as" peut faire tout ce qu'il souhaite tant que le résultat ne peut pas être distingué de la machine abstraite Java). –

1

Il s'agit réellement d'un détail d'implémentation si le contenu actuel de la mémoire d'un objet qui n'est pas synchronisé est visible par un autre thread. Certes, il y a des limites, en ce sens que toute la mémoire n'est pas conservée en double, et toutes les instructions ne sont pas réorganisées, mais le point est que la JVM sous-jacente a l'option si elle est plus optimisée. cette. Le fait est que le tas est vraiment "correctement" stocké dans la mémoire principale, mais l'accès à la mémoire principale est lent par rapport à l'accès au cache de l'UC ou au maintien de la valeur dans un registre à l'intérieur du CPU. En exigeant que la valeur soit écrite en mémoire (ce que fait la synchronisation, au moins quand le verrou est libéré), il force l'écriture dans la mémoire principale. Si la JVM est libre d'ignorer cela, elle peut gagner en performance.

En termes de ce qui va se passer sur un système à une CPU, plusieurs threads peuvent conserver des valeurs dans un cache ou un registre, même pendant l'exécution d'un autre thread. Il n'y a aucune garantie qu'il existe un scénario où une valeur est visible à un autre thread sans synchronisation, bien que cela soit évidemment plus probable.En dehors des appareils mobiles, bien sûr, le processeur unique fonctionne comme des disquettes, ce qui ne sera pas très pertinent pour longtemps.

Pour plus de lecture, je recommande Java Concurrency in Practice. C'est vraiment un excellent livre pratique sur le sujet.

+0

Il a été spécifié dans l'ancien modèle de mémoire Java (JMM), mais cela n'existe plus depuis longtemps. –

+0

Merci pour les commentaires. Cela signifie-t-il que la synchronisation fait simplement référence au vidage du cache du processeur dans la mémoire principale?Ou, y a-t-il des scénarios où la même variable peut exister à deux endroits différents sur le tas? –

+0

@Jack, Non, il peut également se référer à la réorganisation des instructions (donc les choses peuvent être écrites dans la mémoire principale, mais d'une manière qui semblerait être fausse), et bien sûr le verrouillage. Je ne peux pas imaginer qu'une implémentation JVM fasse réellement une copie d'un objet dans la mémoire partagée dans du code non synchronisé, mais je ne connais rien dans la spécification qui ne le permette pas. – Yishai

1

Ce n'est pas aussi simple que CPU-Cache-RAM. Tout cela est enveloppé dans la JVM et le JIT et ils ajoutent leurs propres comportements.

Jetez un oeil à The "Double-Checked Locking is Broken" Declaration. C'est un traité sur pourquoi le verrouillage par double vérification ne fonctionne pas, mais il explique également certaines des nuances du modèle de mémoire de Java.

Questions connexes