Hello,
maybe I am missing something but it seems to me that there is an issue in the vtkJavaMemoryManagerImpl class. Let me describe the scenario:
I am overriding in my Java code the vtkPolygonalHandleRepresentation3D
to add some special behavior:
public class HandleEntityRepresentation extends vtkPolygonalHandleRepresentation3D {
...
}
Then I assign the representation to a widget:
vtkHandleWidget widget;
HandleEntityRepresentation representation;
widget.SetRepresentation(representation);
The crucial point here is that I do not store the reference to representation anywhere in my Java code (local variable only) and fully rely on the vtkHandleWidget.GetHandleRepresentation()
assuming that I can get my HandleEntityRepresentation
back by the call:
(HandleEntityRepresentation) widget.GetHandleRepresentation();
The problem is that sometimes this call ends with the ClassCastException
:
java.lang.ClassCastException: class vtk.vtkPolygonalHandleRepresentation3D cannot be cast to class cz.gsl.gfm.cad.gui.vtk.entity.HandleEntityRepresentation
So first of all I verified, that I have not called the widget.SetRepresentation(representation)
with a vtk.vtkPolygonalHandleRepresentation3D
and I am pretty sure that it is not happening.
Then I started to investigate how the widget.GetHandleRepresentation()
works and get to the vtkJavaMemoryManagerImpl
class. In my opinion the reason of issue I am facing is the
private HashMap<Long, WeakReference<vtkObjectBase>> objectMap;
containing the WeakReference
instances as values. In my case the reference to the representation is not stored anywhere in Java code so GC can assume the referent of its WeakReference
record in objectMap
can be finalized. When
public vtkObjectBase getJavaObject(Long vtkId)
is called value from the objectMap will contain WeakReference with a null and because of that following condition is true:
// We need to do the work of the gc
if (value != null && resultObject == null) {
this.unRegisterJavaObject(vtkId);
}
Finally the record is recreated by the following:
// No-one did create it, so let's do it
if (resultObject == null) {
try {
String className = vtkObjectBase.VTKGetClassNameFromReference(vtkId.longValue());
Class<?> c = Class.forName("vtk." + className);
Constructor<?> cons = c.getConstructor(new Class<?>[] { long.class });
resultObject = (vtkObjectBase) cons.newInstance(new Object[] { vtkId });
} catch (Exception e) {
e.printStackTrace();
}
}
But as a native vtk class instance and so the ClassCastException is thrown.
To avoid such behavior I had to introduce a property
private HandleEntityRepresentation representation;
to my HandleEntityWidget class and method
public void SetRepresentation(final HandleEntityRepresentation representation) {
super.SetRepresentation(representation);
this.representation = representation;
}
to store my representation instance there to prevent the GC to finalize it.
It is a valid solution but was quite difficult to find out what is going on here. So I would suggest to change the vtkJavaMemoryManagerImpl implementation somehow to avoid this kind of issues. I fully understand the reasons why WeakReference is used there but this side effect I am struggling with is unpleasant.
Petr