Odd observation during debugging
Christopher Schultz <[email protected]>
| Newsgroups | gmane.comp.jakarta.velocity.user |
|---|---|
| Message-ID | <[email protected]> |
All,
I recently started getting intermittent OOMEs in my webapp that seem to
be completely recoverable. The stack traces look like this:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2882)
at
java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
at
java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390
)
at java.lang.StringBuilder.append(StringBuilder.java:119)
at java.lang.StringBuilder.append(StringBuilder.java:115)
at
java.util.AbstractCollection.toString(AbstractCollection.java:422)
at
org.apache.velocity.runtime.parser.node.ASTReference.evaluate(ASTReference.java:547)
[...]
This happens so rarely, it's hard to catch, and it's fairly obvious what
is happening: toString is being called on a really huge List. We try not
to use really huge lists, but I suppose there is some potential danger
in rare instances. I want to find those instances.
Velocity 1.7, Tools 2.0 (plus some committed bugfixes that haven't made
it into an official release, yet).
So I thought I'd add some trickery to my VelocityContexts: We use our
own subclasss of VelocityLayoutServlet for a number of things, and I
have overridden the createContext() to wrap the Context the
super.createContext() returns in a ChainedContext (btw, that's
deprecated but doesn't document a replacement -- what should I use?). My
wrapper wraps all List and Set objects in yet another set of wrappers
which essentially pass-through everything except that their toString
methods throw an exception: there should be no reason that a List or Set
should have toString() called on it, because they should only be
iterated over, for instance.
But right on my login page, I see this exception after such instrumentation:
SEVERE: Servlet.service() for servlet velocity threw exception
org.apache.velocity.exception.VelocityException: Reference evaluation
threw an exception at /layout/header.vm[line 18, column 5]
at
org.apache.velocity.runtime.parser.node.ASTReference.evaluate(ASTRefe
rence.java:551)
at
org.apache.velocity.runtime.parser.node.ASTExpression.evaluate(ASTExp
ression.java:62)
at
org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfSt
atement.java:85)
at
org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.
java:342)
at
org.apache.velocity.runtime.directive.Parse.render(Parse.java:260)
at
org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirect
ive.java:207)
at
org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.
java:342)
at org.apache.velocity.Template.merge(Template.java:356)
[....]
Caused by: java.lang.IllegalStateException: Request /myapp/login.vm,
called toString on key 'extraStylesheets'
at
com.chadis.web.servlet.VelocityLayoutServlet$WrappedList.toString(VelocityLayoutServlet.java:220)
at
org.apache.velocity.runtime.parser.node.ASTReference.evaluate(ASTReference.java:547)
If I look at everything that references the key 'extraStylesheets' in my
login page (and the header, etc.), I get only these:
login.vm only includes these references:
#set($extraStylesheets = ['/includes/css/login.css'])
I'm using VelocityLayoutServlet which is using my layouts/Default.vm (no
references to 'extraStylesheets') which includes (via #parse)
'header.vm' which has this to say:
#if($extraStylesheets)
#foreach($stylesheet in $extraStylesheets)
<link type="text/css" href="$link.setRelative($stylesheet)"
rel="stylesheet" />
#end
#end
...and that's it. Given the upper stack trace it seems that the #if() is
evaluating the argument by converting it into a String for evaluation.
Is that intentional? I always used "#if($foo)" to check to see if $foo
had a value of any kind -- that is, if it was non-null (and non-false)
but it appears that the value is being converted into a String for ...
what purpose?
I know a lot of changes have occurred over the years and that
String-conversion is something that has seen some of that action. Is
there a "better" way of determining if a List (or whatever) has a value?
That is, without converting it to a String, first?
One can argue that I shouldn't have Lists of objects so large that
converting them to a String will cause an OOME, but the calling of
toString on these Lists is certainly surprising.
Thanks,
-chris
signature.asc
(application/pgp-signature, 307 B)
-----BEGIN PGP SIGNATURE----- Version: GnuPG/MacGPG2 v2.0.17 (Darwin) Comment: GPGTools - http://gpgtools.org Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAk/9phsACgkQ9CaO5/Lv0PBkHgCfXfztvjiRCQV7U7GQ/0RrNv61 vfEAnjI78qQnn5Uzzi8v0Ine9xDS10lB =UURM -----END PGP SIGNATURE-----