2015-09-07 2 views
0

Je voudrais un peu d'aide pour comprendre comment définir un objet sur null fonctionne dans Java. J'ai une situation où, apparemment, à première vue, il semble qu'un objet qui est défini sur null, n'est soudainement pas nul, mais évidemment, cela ne peut pas être le cas.Définition de l'objet à null et du flux de programme dans Java

J'ai une classe dans laquelle je crée un objet. Cet objet est une scène. Il s'agit d'un projet Open GL ES 2.0, donc les méthodes render() et updateLogic() de cette scène sont appelées depuis onDrawFrame (ceci est contrôlé via un gestionnaire de scène afin que nous puissions facilement changer de scène).

Alors, je pourrais avoir quelque chose comme ça (code abattrez pour le but de la question):

public class MyGLRenderer implements GLSurfaceView.Renderer{ 

    MyScene myScene; 
    SomeOtherScene someOtherScene; 

    public void createScenes(){ 
     myScene = new MyScene(this); 
     someOtherScene = new SomeOtherScene(this); 
     SceneManager.getInstance().setCurrentScene(myScene); 
    } 

    public void cleanUp(){ 
     myScene = null; 
     Log.v("tag","myScene (from MyGLRenderer) is: "+myScene); 
     SceneManager.getInstance().setCurrentScene(someOtherScene); //Scene changed but this won't take effect until the next 'tick' 
    } 

    @Override 
    public void onDrawFrame(GL10 gl) { 
     SceneManager.getInstance().getCurrentScene().updateLogic(); 
     SceneManager.getInstance().getCurrentScene().render(); 
    } 

} 

Dans la situation ci-dessus, le traitement est tourné vers myScene qui ressemblerait à quelque chose comme ça :

public class MyScene implements Scene{ 

    MyGLRenderer renderer; 

    public myScene(MyGLRenderer renderer){  
     this.renderer = renderer; 
    } 

    @Override 
    public void render(){ 
     //Render something here 
    } 

    @Override 
    public void updateLogic(){ 
     doSomething();   
     //The condition here could be anything - maybe the user taps the sceen and a flag is set in onTouchEvent for example 
     if (someConditionIsMet){ 
      renderer.cleanup(); 
     }    
     Log.v("tag","myScene (from within myScene) is: "+this); 
    } 
} 

Alors, quand je mis la scène en utilisant mon manager de scène, le traitement est tourné vers cette scène et il est updateLogic et les méthodes se rendre appelé à partir onDrawFrame en continu. Quand j'ai couru mon code, j'ai été surpris qu'il ne plante pas avec une exception NullpointerException. Les journaux étaient comme ceci:

myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from within myScene) is: [email protected] 
myScene (from MyGLRenderer) is: null 
myScene (from within myScene) is: [email protected] 

Comme vous pouvez le voir, « myScene » est valable jusqu'à la méthode cleanUp() est appelée et fixe à null. Mais le code retourne ensuite à myScene pour finir, où il est toujours valide (pas nul). Je voudrais vraiment comprendre comment fonctionne la chose en Java - pourquoi semble-t-il être nul une minute (ou d'un endroit) et puis pas (d'un endroit différent)?

Répondre

1

Vous venez de supprimer la référence à MyScene dans le moteur de rendu, mais l'objet est toujours là et SceneManager s'y accrochera probablement encore.

La confusion semble être sur "la définition d'un objet à null". Ce n'est pas vraiment possible. Vous pouvez uniquement définir des variables pointant sur des objets à null. Après cela, la variable pointe sur null, mais l'objet peut toujours être présent (comme dans votre cas).

specificially, vous remettons au directeur de scène ici:

SceneManager.getInstance().setCurrentScene(myScene); 

L'objet se ramasse-miettes que si rien ne tient une référence. Dans votre cas, c'est probablement quand le changement de scène prend effet.

+0

Merci @StefanHaustein, OK qui fait genre de sens. Je suis probablement en train de mal comprendre les choses. Donc, parce que l'objet est toujours utilisé (ses méthodes render() et updateLogic() sont toujours appelées), il ne peut pas être GC'd et reste donc actif jusqu'à ce que le système d'exploitation le considère comme libre. '- même si la référence réelle qui lui a été assignée lors de son installation est maintenant nulle (et il n'existe aucune autre' référence 'réelle, c'est-à-dire, comme this.myScene = myScene dans une autre classe quelque part)? – Zippy

+0

Je pense que le gestionnaire de scène s'y tient toujours, a clarifié ma réponse en conséquence. –

+1

Theres reste une référence à l'objet car la pile d'appels doit retourner d'une méthode d'instance sur l'objet. Aussi, comme l'a dit stefan, l'objet est annulé, la référence dans le moteur de rendu est. –

0

On dirait que vous avez rencontré un bug de sécurité.

D'autres threads peuvent afficher les valeurs périmées d'une variable, sauf si vous publiez le changement de valeur en toute sécurité. (Vous avez un processeur qui modifie une valeur dans sa ligne de cache, mais un autre processeur continue de voir des données de cache obsolètes - vous devez forcer le cache à écrire dans la mémoire principale, mais cela coûte cher, donc Java ne le fait pas).

Il existe plusieurs façons de publier une valeur en toute sécurité en fonction de vos besoins exacts. En regardant votre code, il semble que tout ce que vous avez à faire est de déclarer le champ myScene volatile. Il devrait probablement être privé aussi.Essayez donc:

private volatile Scene myScene; 

Si vous voulez bien comprendre la sécurité des threads en Java, je vous recommande chaudement « le Train du livre »: http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601

+0

J'ai essayé de le rendre volatile, mais j'ai les mêmes résultats - pas de crash et ça fonctionne parfaitement. Serait-ce vraiment un problème de sécurité des threads? Je veux dire que je cours sur le même thread (fil GL Rendering), puis en appelant le cleanUp(); méthode du même fil aussi .... – Zippy