Posted By:
Jacob_Marner
Posted On:
Sunday, April 29, 2001 03:03 PM
Hi, I have summitted a feature request to the java.sun.com feature request forum and was asked to post it a forum get other people's opinion first. That is what I am doing now. Please comment on whether I have misunderstood something, missed something or whether I am right. ------------- Note: This text uses garbage collector terms from the book: Java platform performance, appendix A, which is available on-line from Sun. The main issue that I wish to address is the fact that is you access an object via a weak reference in native code (with JNI) then you have no way of telling whether t
More>>
Hi,
I have summitted a feature request to the java.sun.com feature
request forum and was asked to post it a forum get other people's
opinion first. That is what I am doing now.
Please comment on whether I have misunderstood something,
missed something or whether I am right.
-------------
Note: This text uses garbage collector terms from the book: Java platform
performance, appendix A, which is available on-line from Sun.
The main issue that I wish to address is the fact that is you access an
object via a weak reference in native code (with JNI) then you
have no way of telling whether the object has been finalized.
I suggest the JNI function NewLocalRef() and NewGlobalRef() is changed such that
using it on a weak reference obj it returns only non-null if the object
has not yet been collected.
I have not used the java.lang.ref.* objects but as far as I understand
from the documentation an object is no longer weakly reachable when
it has been collected. After this it might be finalized at any time. It
therefore makes sense to avoid the use of weak references that
points to objects not weakly referenced.
---------------
*** The problem this issue causes ***
Currently we have only one way to use an object referenced
with a weak reference in JNI:
jobject jobj = env->NewLocalRef(obj); // obj is a weak reference.
if (!env->IsSameObject(jobj,NULL))
... use jobj ...
We make a strong reference in the call to NewLocalRef() to make a strong
reference to avoid the case when the weakly referenced object is collected
after the call to IsSameObject() and its use.
In the above situation NewLocalRef() returns only NULL when the object
has been reclaimed by the system. This means that we might make a strong
reference to an object that has already been finalized or are queued for
finalization. In fact, it might be finalized at this instant if a VM that
runs finalizers in seperate threads are used.
In my case I have weak references to objects that has external resources
that must reclaimed upon finalization. The object has no external resources
after finalization and should therefore not be used. It is *not* enough
to set some variable in the finalize() method to say that the external
resources are released - because if the finalizer thread is interrupted
in its first line of finalize() this value is not set.
workaround : A temporary, not fully satisfactory, workaround is to delete
all weak references to an object from its finalizer. It may still go wrong
but chances are we will not use a that weak references to an object
that has been finalized.
---------------------
The full source of an example that shows how this goes wrong:
(Used Sun JDK 1.3.0_02 on Windows 98. Used Microsoft Visual C++
6.0 to compile the native part)
Bad.java:
============
class Bad
{
boolean legal = true;
protected void finalize()
{
legal = false;
// external clean-up goes here. The use of the
// object after this point is an error.
}
public static void main(String[] args)
{
System.loadLibrary("bug");
int count = 1;
while (true)
{
//System.gc(); // Uncomment this to keep garbage collector
// up to date. Avoids problem in most cases.
System.out.println(count);
generateReferences(count);
retrieveReferences(count);
count++;
}
}
public static void generateReferences(int count)
{
for (int i=0;i
{
Bad object = new Bad();
n_storeReference(object);
}
}
native static void n_storeReference(Bad object);
public static void retrieveReferences(int count)
{
for (int i=0;i
{
Bad object = n_retrieveReference();
if (object!=null && !object.legal)
{
System.out.println("Error! Object already finalized.");
System.exit(1);
}
// This is where we would use the object. If it is
// finalized then the external data is illegal.
}
}
native static Bad n_retrieveReference();
}
And the C++ files native.cpp that becomes bug.dll:
===================================
#include "Bad.h"
#include
std::list
objectlist;
JNIEXPORT void JNICALL Java_Bad_n_1storeReference
(JNIEnv *env, jclass, jobject jobj)
{
jobject o = env->NewWeakGlobalRef(jobj);
objectlist.push_front(o);
}
JNIEXPORT jobject JNICALL Java_Bad_n_1retrieveReference
(JNIEnv *env, jclass)
{
if(!objectlist.empty())
{
jobject jobj = env->NewLocalRef(objectlist.back());
objectlist.pop_back();
if (!env->IsSameObject(jobj,NULL))
return jobj;
}
return NULL;
}
This program needs to be run for a while until the timing
of the use of the weak reference is right. On my computer it
happens after 120 iterations. If you turn on the call to
System.gc() it takes so long that I lost patience.
-----------
What do you think? Dou agree on this issue, or have I overlooked
something. Please comment.
B.Sc. Jacob Marner
<<Less