RFR(M): 8203826: Chain class initialization exceptions into later NoClassDefFoundErrors
Peter Levart
peter.levart at gmail.com
Wed Jul 4 10:39:20 UTC 2018
Hi Volker,
It occurred to me that getting rid of backtrace-s of cause(s)/suppressed
exception(s) might not be enough to prevent ClassLoader leaks...
On 07/04/2018 10:21 AM, Lindenmaier, Goetz wrote:
>> dealing with backtrace and stackTrace. I have to wonder why nothing in
>> Throwable clears the backtrace today ?
> Maybe the concern about the backTraces is pointless and the
> conversion to stackTraces should be dropped.
> As you say, it's done nowhere else, and other backTraces should
> cause similar issues.
>
Exception objects are typically not retained for longer periods. They
are normally caught, dumped to log and let gone. This change retains
exception(s) so that they are reachable from a ClassLoader that loaded
the failed class. It could happen that the chain of cause(s)/suppressed
exception(s) of some ExceptionInInitializerError is an exception object
of a class that is loaded by some child ClassLoader of the ClassLoader
that loaded the failed class. Such child ClassLoader would have leaked.
The solution would be to replace the chain of cause(s)/suppressed
exception(s) with a chain of replacement exception objects like this one
(this would also take care of backtraces of original exceptions as it
would not retain the original exceptions at all):
/**
* A {@link RuntimeException} that acts as a substitute for the
original exception
* (checked or unchecked) and mimics the original exception in every
aspect except it's type.
*/
public class ExceptionSubstitute extends RuntimeException {
private static final long serialVersionUID = 1;
private String originalExceptionClassName, localizedMessage;
public ExceptionSubstitute(Throwable originalException) {
super(originalException.getMessage());
this.originalExceptionClassName =
originalException.getClass().getName();
this.localizedMessage = originalException.getLocalizedMessage();
// substitute originalException's cause
Throwable cause = originalException.getCause();
initCause(cause == null ? null : new ExceptionSubstitute(cause));
// substitute originalException's suppressed exceptions if any
for (Throwable suppressed : originalException.getSuppressed()) {
addSuppressed(new ExceptionSubstitute(suppressed));
}
// inherit stack trace elements from originalException
setStackTrace(originalException.getStackTrace());
}
@Override
public Throwable fillInStackTrace() {
// don't need our backtrace - will inherit stack trace elements
from originalException
return this;
}
@Override
public String getLocalizedMessage() {
return localizedMessage;
}
/**
* @return the class name of the original exception for which this
exception is a substitute
*/
public String getOriginalExceptionClassName() {
return originalExceptionClassName;
}
/**
* Emulate toString() method as if called upon originalException
*/
@Override
public String toString() {
String message = getLocalizedMessage();
return (message != null)
? (getOriginalExceptionClassName() + ": " + message)
: getOriginalExceptionClassName();
}
}
Regards, Peter
More information about the hotspot-runtime-dev
mailing list