Re: Odd observation during debugging
Nathan Bubna <[email protected]>
| Newsgroups | gmane.comp.jakarta.velocity.user |
|---|---|
| Message-ID | <CAFyaDjE8BFW-RviZy2hNkKTr4Q2fyjBi8-fFiX1uhdpJtiMsqA@mail.gmail.com> |
Just set: directive.if.tostring.nullcheck = false See VELOCITY-731 and VELOCITY-692 and others... On Wed, Jul 11, 2012 at 9:13 AM, Christopher Schultz <[email protected]> wrote: > 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 >