@@ -73,14 +73,17 @@
* {@code object.getClass().getName() } kept around so that we can see the type even after it
* gets deallocated.
*/
@Nonnull
private final String objectType;
/**
* Where was this object first exported?
*/
@Nonnull
final CreatedAt allocationTrace;
/**
* Where was this object unexported?
*/
@CheckForNull
ReleasedAt releaseTrace;
/**
* Current reference count.
@@ -92,9 +95,10 @@
/**
* This field can be set programmatically to track reference counting
*/
@CheckForNull
private ReferenceCountRecorder recorder;
Entry (T object , Class<? super T > ... interfaces ) {
Entry (@Nonnull T object , Class<? super T > ... interfaces ) {
this . id = iota++ ;
this . interfaces = interfaces. clone();
this . object = object;
@@ -122,7 +126,13 @@ void pin() {
referenceCount += Integer . MAX_VALUE/ 2 ;
}
void release (Throwable callSite ) {
/**
* Releases the entry.
* @param callSite
* Optional location that indicates where the actual call site was that triggered the activity,
* in case it was requested from the other side of the channel.
*/
void release (@CheckForNull Throwable callSite ) {
if (recorder!= null )
recorder. onRelease(callSite);
@@ -204,7 +214,7 @@ synchronized void addInterface(Class<? super T> clazz) {
* Optional location that indicates where the actual call site was that triggered the activity,
* in case it was requested from the other side of the channel.
*/
Source (Throwable callSite ) {
Source (@CheckForNull Throwable callSite ) {
super (callSite);
// force the computation of the stack trace in a Java friendly data structure,
// so that the call stack can be seen from the heap dump after the fact.
@@ -217,16 +227,18 @@ synchronized void addInterface(Class<? super T> clazz) {
super (null );
}
@Override
public String toString () {
return " Created at " + new Date (timestamp);
}
}
static class ReleasedAt extends Source {
ReleasedAt (Throwable callSite ) {
ReleasedAt (@CheckForNull Throwable callSite ) {
super (callSite);
}
@Override
public String toString () {
return " Released at " + new Date (timestamp);
}
@@ -288,19 +300,26 @@ boolean isRecording() {
* @return
* The assigned 'object ID'. If the object is already exported,
* it will return the ID already assigned to it.
* @param clazz
* @param t
* {@code 0 } if the input parameter is {@code null }.
* @param clazz Class of the object
* @param t Class instance
*/
synchronized <T > int export (Class<T > clazz , T t ) {
synchronized <T > int export (@Nonnull Class<T > clazz , @CheckForNull T t ) {
return export(clazz, t,true );
}
/**
* @param clazz
* Exports the given object.
* @param clazz Class of the object
* @param t Object to be exported
* @param notifyListener
* If false, listener will not be notified. This is used to
* @return
* The assigned 'object ID'. If the object is already exported,
* it will return the ID already assigned to it.
* {@code 0 } if the input parameter is {@code null }.
*/
synchronized <T > int export (Class<T > clazz , T t , boolean notifyListener ) {
synchronized <T > int export (@Nonnull Class<T > clazz , @CheckForNull T t , boolean notifyListener ) {
if (t== null ) return 0 ; // bootstrap classloader
Entry e = reverse. get(t);
@@ -319,14 +338,21 @@ boolean isRecording() {
return e. id;
}
/* package*/ synchronized void pin (Object t ) {
/* package*/ synchronized void pin (@Nonnull Object t ) {
Entry e = reverse. get(t);
if (e!= null )
e. pin();
}
synchronized @Nonnull
Object get (int id ) throws ExecutionException {
/**
* Retrieves object by id.
* @param id Object ID
* @return Object
* @throws ExecutionException The requested ID cannot be found.
* The root cause will be diagnosed by {@link #diagnoseInvalidObjectId(int)}.
*/
@Nonnull
synchronized Object get (int id ) throws ExecutionException {
Entry e = table. get(id);
if (e!= null ) return e. object;
@@ -347,8 +373,8 @@ synchronized Object getOrNull(int oid) {
return null ;
}
synchronized @Nonnull
Class [] type (int id ) throws ExecutionException {
@Nonnull
synchronized Class [] type (int id ) throws ExecutionException {
Entry e = table. get(id);
if (e!= null ) return e. getInterfaces();
@@ -362,8 +388,11 @@ synchronized Object getOrNull(int oid) {
* Exported {@link Pipe}s are vulnerable to infinite blocking
* when the channel is lost and the sender side is cut off. The reader
* end will not see that the writer has disappeared.
*
* @param e Termination error
*
*/
void abort (Throwable e ) {
void abort (@CheckForNull Throwable e ) {
List<Entry<?> > values;
synchronized (this ) {
values = new ArrayList<Entry<?> > (table. values());
@@ -392,6 +421,7 @@ void abort(Throwable e) {
* @param id Object ID
* @return Exception to be thrown
*/
@Nonnull
private synchronized ExecutionException diagnoseInvalidObjectId (int id ) {
Exception cause= null ;
@@ -410,8 +440,10 @@ private synchronized ExecutionException diagnoseInvalidObjectId(int id) {
/**
* Removes the exported object from the table.
* @param t Object to be unexported. {@code null } instances will be ignored.
* @param callSite Stacktrace of the invocation source
*/
synchronized void unexport (Object t , Throwable callSite ) {
synchronized void unexport (@CheckForNull Object t , Throwable callSite ) {
if (t== null ) return ;
Entry e = reverse. get(t);
if (e== null ) {
@@ -431,12 +463,12 @@ void unexportByOid(Integer oid, Throwable callSite) {
/**
* Removes the exported object for the specified oid from the table.
* @param oid Object ID
* @param oid Object ID. If { @code null } the method will do nothing.
* @param callSite Unexport command caller
* @param severeErrorIfMissing Consider missing object as {@link #SEVERE} error. {@link #FINE} otherwise
* @since TODO
*/
synchronized void unexportByOid (Integer oid , Throwable callSite , boolean severeErrorIfMissing ) {
synchronized void unexportByOid (@CheckForNull Integer oid , @CheckForNull Throwable callSite , boolean severeErrorIfMissing ) {
if (oid== null ) return ;
Entry e = table. get(oid);
if (e== null ) {
@@ -451,8 +483,9 @@ synchronized void unexportByOid(Integer oid, Throwable callSite, boolean severeE
/**
* Dumps the contents of the table to a file.
* @throws IOException Output error
*/
synchronized void dump (PrintWriter w ) throws IOException {
synchronized void dump (@Nonnull PrintWriter w ) throws IOException {
for (Entry e : table. values()) {
e. dump(w);
}
@@ -462,6 +495,10 @@ synchronized void dump(PrintWriter w) throws IOException {
return reverse. containsKey(o);
}
/**
* Defines number of entries to be stored in the unexport history.
* @since 2.40
*/
public static int UNEXPORT_LOG_SIZE = Integer . getInteger(ExportTable . class. getName()+ " .unexportLogSize" ,1024 );
private static final Logger LOGGER = Logger . getLogger(ExportTable . class. getName());