Name: jk109818 Date: 01/23/2002
FULL PRODUCT VERSION :
java version "1.4.0-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-rc-b91)
Java HotSpot(TM) Client VM (build 1.4.0-rc-b91, mixed mode)
FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]
A DESCRIPTION OF THE PROBLEM :
** Note: This is a resubmission that repeats one made about
a year ago but never made it to the bug database. Since
then Jeff Kesselman from Sun has confirmed that this is in
fact a bug so please take it seriously. **
****** Overview *******
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 finalization process has
begun and
cannot be stopped.
I suggest the JNI function NewLocalRef(env,obj) is changed
such that
using it on a weak reference obj it returns only non-null
if the object
has not yet been collected, unlike now where it return non-
null until the
object is finally reclaimed. This change is an
implementation change that can
be changed without changing the specification.
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. In other words, by
making the change I
suggest you make the system more consistant by avoiding
that they user
references phantom referenced objects using JNI weak
references.
---------------
*** The problem in more detail ***
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
avoid the case when
the weakly referenced object is collected after the call to
IsSameObject() and
before its use. (can happen if a asynchronous garbage
collector is used)
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.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See source code below.
EXPECTED VERSUS ACTUAL BEHAVIOR :
I would expect that a reference could not access an object
that has finalized. This is what occurs in the given source
code sample.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Upon execution the sample program writes (only the last part is shown):
...
93
94
95
96
97
98
99
100
101
102
103
104
105
Error! Object already finalized.
This bug can be reproduced rarely.
---------- BEGIN SOURCE ----------
******* Source of example *************
The full source of an example that shows how this goes wrong:
(This was detected in version 1.2.2 of the JDK and has been tested with each
version since then until finally 1.4.0 rc. It was present in all versions. It
has been tested on both Windows 98 and Windows 2000. I used Microsoft Visual C++
6.0 to compile the native part)
This simple 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 or 105 iterations depending on the system. If you
turn on the commented call to
System.gc() it takes so long that I lost patience, but this is just a hack to
postpone that the program crashes.
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<count;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<count;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 <list>
std::list<jobject> 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;
}
---------- END SOURCE ----------
CUSTOMER 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.
(Review ID: 138679)
======================================================================