Avoid creating a stacktrace prior to JDK 1.7

J

Jan Burse

Dear All,

I have the following code deep down in a
recursion which eats up a lot of stack.
The recursion is not shown but only
the code:

Class<?> class;
try {
class = Class.forName(str);
} catch (ClassNotFoundException x) {
class = null;
}

Will it every time I call it for a
non-existing class build the whole stack
trace for the exception x and then
immediately forget about it?

Now I see in JDK 1.7 (*) a protected
constructor:

package java.lang;

protected Throwable(String message,
Throwable cause,
boolean enableSuppression,
boolean writableStackTrace)

If I were to write my own forName()
method I would set the writableStackTrace
to false if the exception is only needed
to immediately notify the callee about
something.

But what if I have JDK 1.6 only available
and not JDK 1.7. Can I instruct an exception
to not fill the stack trace? And way
arround?

Bye

(*)
http://download.oracle.com/javase/7/docs/api/
 
S

Stanimir Stamenkov

Fri, 30 Sep 2011 15:57:57 +0200, /Jan Burse/:
I have the following code deep down in a
recursion which eats up a lot of stack.
The recursion is not shown but only
the code:

Class<?> class;
try {
class = Class.forName(str);
} catch (ClassNotFoundException x) {
class = null;
}

Will it every time I call it for a
non-existing class build the whole stack
trace for the exception x and then
immediately forget about it?

I guess so.
[...]
Can I instruct an exception
to not fill the stack trace? And way
arround?

You may perform a check which avoids the exceptional situation like:

Class<?> class;
try {
class = (Class.getResource(str + ".class") != null)
? Class.forName(str)
: null;
} catch (ClassNotFoundException x) {
class = null;
}

The Class.forName(String) could still fail if the access to the
resource content is restricted, I imagine.
 
J

Jan Burse

Stanimir said:
Fri, 30 Sep 2011 15:57:57 +0200, /Jan Burse/:
I have the following code deep down in a
recursion which eats up a lot of stack.
The recursion is not shown but only
the code:

Class<?> class;
try {
class = Class.forName(str);
} catch (ClassNotFoundException x) {
class = null;
}

Will it every time I call it for a
non-existing class build the whole stack
trace for the exception x and then
immediately forget about it?

I guess so.
[...]
Can I instruct an exception
to not fill the stack trace? And way
arround?

You may perform a check which avoids the exceptional situation like:

Class<?> class;
try {
class = (Class.getResource(str + ".class") != null)
? Class.forName(str)
: null;
} catch (ClassNotFoundException x) {
class = null;
}

The Class.forName(String) could still fail if the access to the resource
content is restricted, I imagine.

Yes, this would change the role of exceptions,
not use it for business logic, only for
exceptional flow.

gr8, thx

Bye
 
L

Lew

Jan said:
Yes, this would change the role of exceptions,

No, it would /restore/ the role of exceptions.

The role of exceptions is to
not use it for business logic, only for
exceptional flow.

in the first place.

/Effective Java/, Joshua Bloch, "Item 57: Use exceptions only for exceptional conditions".
 
S

Stanimir Stamenkov

Sat, 01 Oct 2011 13:27:39 +0300, /Stanimir Stamenkov/:
You may perform a check which avoids the exceptional situation like:

Class<?> class;
try {
class = (Class.getResource(str + ".class") != null)

I guess the above line should be rewritten like:

String resourceName = '/' + str.replace('.', '/') + ".class";
class = (Class.getResource(resourceName) != null)

to function properly.
 
S

Stanimir Stamenkov

Sat, 1 Oct 2011 09:10:16 -0700 (PDT), /Lew/:
No, it would /restore/ the role of exceptions.

The role of exceptions is to

in the first place.

/Effective Java/, Joshua Bloch, "Item 57: Use exceptions only for exceptional conditions".

I don't think handling the ClassNotFoundException from
Class.forName(String) contradicts the above statement. In this
exact case one may consider handling the exception a better
alternative if the code in question is invoked multiple times with
possibly the same class name, which class could have been
initialized already and Class.forName() would just return the Class
object. The resource lookup may be more expensive operation, if
repeated every time, just to avoid the exception. One may also
cache the resource lookup results.
 
J

Jan Burse

Lew said:
in the first place.

/Effective Java/, Joshua Bloch, "Item 57: Use exceptions only for exceptional conditions".

The JDK itself violates this rule. For example
each time a Thread is created the following
check is run:

/**
* Performs reflective checks on given subclass to verify
* that it doesn't override security-sensitive non-final
* methods. Returns true if the subclass overrides any of
* the methods, false otherwise.
*/
private static boolean auditSubclass(final Class subcl) {
Boolean result = AccessController.doPrivileged(
new PrivilegedAction<Boolean>() {
public Boolean run() {
for (Class cl = subcl;
cl != Thread.class;
cl = cl.getSuperclass())
{
try {

cl.getDeclaredMethod("getContextClassLoader", new Class[0]);
return Boolean.TRUE;
} catch (NoSuchMethodException ex) {
}
try {
Class[] params = {ClassLoader.class};

cl.getDeclaredMethod("setContextClassLoader", params);
return Boolean.TRUE;
} catch (NoSuchMethodException ex) {
}
}
return Boolean.FALSE;
}
}
);
return result.booleanValue();
}

In the above the sunshine flow is that we get a boolean
value false, which means that two NoSuchMethodExceptions
are thrown. It seems that in connection with the reflection
API using the exceptions for business logic has become
the prefered idiom contrary to the advice.

But this is probably due to a lack of a better reflection
API. Or we can even analysis it deeper. Since the reflection
API methods can return so much information AND since java
does not have multi valued returns, the exceptions have
been abused for returning additional information.

In the Go Programming language one would do something:

getDeclaredMethod(String,Class[]) (Method,Error)

Shit happens!

P.S.: Actually the situation is worse in JDK 1.7, the
audit is done but then cached during the call of
isCCLOverridden(). This is good. But each call of
isCCLOverridden() does also poll the cache for
removal of inaccessible keys! Hm, not sure what
I should think about that.

Bye
 
J

Jan Burse

Hi

And how about suppressing back trace fetch in
an exception class, can this be done prior to
JDK 1.6?

Bye
 
S

Stanimir Stamenkov

Sat, 01 Oct 2011 20:04:29 +0200, /Jan Burse/:
And how about suppressing back trace fetch in
an exception class, can this be done prior to
JDK 1.6?

May be just as global JVM specific setting, but I'm not aware of one.
 
L

Lew

Jan said:
Lew said:
in the first place.

/Effective Java/, Joshua Bloch, "Item 57: Use exceptions only for exceptional conditions".

The JDK itself violates this rule. For example
each time a Thread is created the following
check is run:
... [snip excellent example] ...

In the above the sunshine flow is that we get a boolean
value false, which means that two NoSuchMethodExceptions
are thrown. It seems that in connection with the reflection
API using the exceptions for business logic has become
the prefered idiom contrary to the advice.

But this is probably due to a lack of a better reflection
API. Or we can even analysis it deeper. Since the reflection
API methods can return so much information AND since java
does not have multi valued returns, the exceptions have
been abused for returning additional information.

In the Go Programming language one would do something:

getDeclaredMethod(String,Class[]) (Method,Error)

Shit happens!

P.S.: Actually the situation is worse in JDK 1.7, the
audit is done but then cached during the call of
isCCLOverridden(). This is good. But each call of
isCCLOverridden() does also poll the cache for
removal of inaccessible keys! Hm, not sure what
I should think about that.

Excellent analysis and conclusions.

In bringing up the rule "Use exceptions only for exceptional conditions" [/op cit./] I omitted to mention another important rule, or set of rules:
- There are no universal rules.
and its corollaries/consequents,
- Rules were made to be broken.
- The virtuoso knows when to break the rules. (The master creates the rules.)
- The exception probes the rule. [original intent of the cliché preserved]

Like all the engineering decisions we make, the use of exceptions balances alternatives. One could argue that the conditions you showed in your example were "exceptional" enough to merit handling by exception. Pragmatically, the alternatives suck by comparison anyway. Y'know, that motivating "exceptional conditions" clause leaves a whole lotta wiggle room.

It's a common joke in engineering disciplines to issue harsh directives that boil down to, "Do what thou wilt, but use good judgment." This does not detract from the wisdom of the advice.
 
J

Jan Burse

Lew said:
- Rules were made to be broken.

Yes, a well known common place.

Now back to the original question of my
post, I was giving the ClassNotFoundException
only as a motivation, can I suppress the
fetching of the backtrace in a Java
Exception object prior to JDK 1.7?

Bye
 
E

Eric Sosman

Yes, a well known common place.

Now back to the original question of my
post, I was giving the ClassNotFoundException
only as a motivation, can I suppress the
fetching of the backtrace in a Java
Exception object prior to JDK 1.7?

Quoth the Java SE 6 Javadoc: "A throwable [sic] contains a
snapshot of the execution stack of its thread at the time it was
created." That is, the Javadoc promises that a Throwable carries
a stack trace. A Throwable with no stack trace breaches the
promise, and since that promise was still in force as of 1.6 I
deduce that there's no such thing as a stackless Throwable in that
version, or that it's a bug if there is.

However, it's the trace of the stack that *creates* the
Throwable, not necessarily that of the stack that *throws* it.
So you could create a Throwable once and throw it as many times
from as many different contexts as you like:

class MasochismInAction {
private static OriginUnknownException mystery =
new OriginUnknownException("Ha-ha, can't find me!");
public void foolMeOnce() throws OriginUnknownException {
if (iFeelLikeIt) {
throw mystery;
}
}
public void foolMeTwice() throws OriginUnknownException {
if (Math.random() < 0.3) {
throw mystery;
}
}
...
}

.... and there you have it: Exceptions without (most of) the expense
of filling a stack trace. Also, it must be noted, Exceptions with
less ability to help in debugging than almost any others. ("Almost"
because I've heard somewhere that the JVM creates a few Throwables
like VirtualMachineError in advance, because by the time they're
needed the JVM can no longer trust itself to fill them in properly.)

Personally, I think "Don't Do That" is sage advice here.
 
J

Jan Burse

Eric said:
... and there you have it: Exceptions without (most of) the expense
of filling a stack trace. Also, it must be noted, Exceptions with
less ability to help in debugging than almost any others.

Actually this is a good solution. To pre-allocate exceptions
with a small stack trace! Thank you for the good idea.

The solution could be even spun further. Why not create
a separate thread, that will provide a service that
will create exceptions without a big stack trace. Will
need a little synchronize, but could also work.
Personally, I think "Don't Do That" is sage advice here.

Well it exists in JDK 1.7 for some reason. But you cannot
do it off hand, you can only do it for your own exceptions
since the constructor is protected.

I guess a typical usage scenario would be the creation
of a stack overflow exception or a memory exception, so
as to allow the application to recover.

Usually a stack overflow exception is not so interesting,
most often it is caused by a loop that starts somewhere
and all you have is the same method displayed over an over.
("Almost" because I've heard somewhere that the JVM
creates a few Throwables like VirtualMachineError in
advance, because by the time they're needed the JVM
can no longer trust itself to fill them in properly.)

gr8, one more business case!

Bye
 
E

Eric Sosman

Eric said:
... and there you have it: Exceptions without (most of) the expense
of filling a stack trace. Also, it must be noted, Exceptions with
less ability to help in debugging than almost any others.

Actually this is a good solution. To pre-allocate exceptions
with a small stack trace! Thank you for the good idea.
[...]
Personally, I think "Don't Do That" is sage advice here.

I'm sticking with DDT. You are doing yourself no favors by
working hard to *prevent* Java from helping you find bugs.
 
S

Stanimir Stamenkov

Sat, 01 Oct 2011 16:24:46 -0400, /Eric Sosman/:
Quoth the Java SE 6 Javadoc: "A throwable [sic] contains a
snapshot of the execution stack of its thread at the time it was
created." That is, the Javadoc promises that a Throwable carries
a stack trace. A Throwable with no stack trace breaches the
promise, and since that promise was still in force as of 1.6 I
deduce that there's no such thing as a stackless Throwable in that
version, or that it's a bug if there is.

You may be right, but I've read in the Javadoc for
java.util.logging.Logger, for example
<http://download.oracle.com/javase/6/docs/api/java/util/logging/Logger.html>:

| For the methods that do not take an explicit source name and
| method name, the Logging framework will make a "best effort" to
| determine which class and method called into the logging method.
| However, it is important to realize that this automatically
| inferred information may only be approximate (or may even be
| quite wrong!). Virtual machines are allowed to do extensive
| optimizations when JITing and may entirely remove stack frames,
| making it impossible to reliably locate the calling class and
| method.

So it seems stack trace information is not guaranteed to be
available to user code. This detail may be given somewhere in the
language or VM specifications.
 
A

Andreas Leitgeb

Jan Burse said:
Actually this is a good solution. To pre-allocate exceptions
with a small stack trace! Thank you for the good idea.

I just can't see, how you'd make Class.forName(...) throw your
pre-allocated exception rather than a new one.

If anything were to be changed in forName(), then it might
rather be offering a variant that returns null instead of
throwing an exception. But then again, I think, this particular
scenario(*) is rare enough that it doesn't really matter.

(*): namely the scenario of bulk-classloading requiring a cheap
fail-path.
 
J

Jan Burse

Stanimir said:
quite wrong!). Virtual machines are allowed to do extensive
| optimizations when JITing and may entirely remove stack frames,
| making it impossible to reliably locate the calling class and
| method.

So it seems stack trace information is not guaranteed to be available to
user code. This detail may be given somewhere in the language or VM
specifications.

Yes, you usually don't get a stack overflow
when you do tail recursion for example. So
a method:

void p(int a) {

...
p(b);
}

Is the same as a loop:

void p(int a) {
for (;;) {
...
a = b;
}
}

The simplest obtimisation a JIT can do is replace
the last call p(b) by a jump, so not pushing a
new return address on the stack and reusing the
already pushed return address.

Etc... Etc...

Bye
 
L

Lew

Stanimir said:
You may be right, but I've read in the Javadoc for
java.util.logging.Logger, for example
<http://download.oracle.com/javase/6/docs/api/java/util/logging/Logger.html>:

| For the methods that do not take an explicit source name and
| method name, the Logging framework will make a "best effort" to
| determine which class and method called into the logging method.
| However, it is important to realize that this automatically
| inferred information may only be approximate (or may even be
| quite wrong!). Virtual machines are allowed to do extensive
| optimizations when JITing and may entirely remove stack frames,
| making it impossible to reliably locate the calling class and
| method.

So it seems stack trace information is not guaranteed to be
available to user code. This detail may be given somewhere in the
language or VM specifications.

Stack trace information is available; the difficulty to which the documentation alludes is that the stack might be in an odd state, not that it wouldn't be available. So it seems stack trace information is guaranteed to be available. Just not necessarily useful in the way you'd like.
 
S

Stanimir Stamenkov

Sat, 1 Oct 2011 20:06:23 -0700 (PDT), /Lew/:
Stanimir said:
You may be right, but I've read in the Javadoc for
java.util.logging.Logger, for example
<http://download.oracle.com/javase/6/docs/api/java/util/logging/Logger.html>:
[...]
So it seems stack trace information is not guaranteed to be
available to user code. This detail may be given somewhere in the
language or VM specifications.

Stack trace information is available; the difficulty to which the
documentation alludes is that the stack might be in an odd state,
not that it wouldn't be available. So it seems stack trace
information is guaranteed to be available. Just not necessarily
useful in the way you'd like.

So while Throwable.getStackTrace() might not be null it could be
empty which effectively means no stack trace information available
<http://download.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace()>:

| Some virtual machines may, under some circumstances, omit one or
| more stack frames from the stack trace. In the extreme case, a
| virtual machine that has no stack trace information concerning
| this throwable is permitted to return a zero-length array from
| this method.
 
L

Lew

Stanimir said:
/Lew/:
Stanimir said:
You may be right, but I've read in the Javadoc for
java.util.logging.Logger, for example
<http://download.oracle.com/javase/6/docs/api/java/util/logging/Logger.html>:
[...]
So it seems stack trace information is not guaranteed to be
available to user code. This detail may be given somewhere in the
language or VM specifications.

Stack trace information is available; the difficulty to which the
documentation alludes is that the stack might be in an odd state,
not that it wouldn't be available. So it seems stack trace
information is guaranteed to be available. Just not necessarily
useful in the way you'd like.

So while Throwable.getStackTrace() might not be null it could be
empty which effectively means no stack trace information available
<http://download.oracle.com/javase/6/docs/api/java/lang/Throwable.html#getStackTrace()>:

| Some virtual machines may, under some circumstances, omit one or
| more stack frames from the stack trace. In the extreme case, a
| virtual machine that has no stack trace information concerning
| this throwable is permitted to return a zero-length array from
| this method.

Right, but in those "extreme" cases you have a situation where no stack trace information is available to be made available, if you get my drift. Andthey don't help the OP, because he's trying to suppress stack trace information when it could be made available, and he can't count on there being nostack trace available to be available.

My point was that it isn't quite accurate to say that stack trace information isn't available to user code - in the even that there is any stack traceinformation, that is. If there isn't, the Throwable at least reports thatthere is nothing to report, which gives the programmer more information than would a failure to report.

So it seems stack trace information is guaranteed to be available. Just not necessarily useful in the way you'd like. But still useful to some degree, more than not making available whatever paucity of information there is.

There's a difference between "no information" and "information that there is no information". The latter tells you something, at least.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top