When loadtesting our site with 10 users to a page which makes a lot of use of the fmt:formatdate, the hits / seconds drop and fluctuate tremondously between 5 hits / sec and 20 hits per sec. With 1 or 2 users the hits per second maintain constantly at 20 hits / sec. Using Weblogic 6.1 SP4, with 15 (default) execute threads, memory / garbage collection is not a problem. After creating threaddumps is appears that almost all threads have these stacktraces on the top: at java.util.Calendar.getInstance(Calendar.java:829) at java.text.SimpleDateFormat.initialize(SimpleDateFormat.java:326) at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:317) at java.text.DateFormat.get(DateFormat.java:645) at java.text.DateFormat.getTimeInstance(DateFormat.java:411) at org.apache.taglibs.standard.tag.common.fmt.FormatDateSupport.createFormatter(FormatDateSupport.java:223) at org.apache.taglibs.standard.tag.common.fmt.FormatDateSupport.doEndTag(FormatDateSupport.java:155) And are all "waiting on monitor XXXXX", it seems these calls are blocking each other, possible due to the fact that getInstance is synchronized? Other pages which are not using this tag are not a problem.
Since the date patterns, are likely to stay the same on each page. (Because usually the date is dynamic, and the date patterns static). Would it be an idea to have a pool of SimpleDateFormatters each with its own pattern + locale, so we could reuse the same patterns to avoid unnecessary calls to Calendar.getInstance? e.g. in org.apache.taglibs.standard.tag.common.fmt.FormatDateSupport Something like: static Map sdfCache=new HashMap(); public synchronized SimpleDateFormat getSimpleDateFormat(String pattern, Locale locale) { System.out.println("getSimpleDateFormat "+pattern+":"+locale); String key=pattern+locale; SimpleDateFormat sdf; if (sdfCache.containsKey(key)) { sdf=(SimpleDateFormat) sdfCache.get(key); } else { sdf=new SimpleDateFormat(pattern, locale); sdfCache.put(key,sdf); } return sdf; } And later: Instead of DateFormat formatter = createFormatter(locale); // Apply pattern, if present if (pattern != null) { try { ((SimpleDateFormat) formatter).applyPattern(pattern); } catch (ClassCastException cce) { formatter = new SimpleDateFormat(pattern, locale); } } Something like DateFormat formatter = getSimpleDateFormat (pattern, locale);
After making a prototype of the code mentioned earlier, I saw a tremendous increase in the performance; and no calendar.getInstance appeared in the stacktrace... However... Now we are experiencing locking issues with this method: org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager.getEvaluator ByName(ExpressionEvaluatorManager.java:144) org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager.evaluate (ExpressionEvaluatorManager.java:128) Fortunatly I don't seem to be alone anymore with this problem: http://www.mail-archive.com/taglibs-user@jakarta.apache.org/msg04179.html Is there any progress on this? (After all the above message was posted over 1 year ago)
Ah the above locking seems to be resolved in 1.0.6: http://jakarta.apache.org/taglibs/doc/standard-1.0-doc/ReleaseNotes.html
The suggestion below of a static cache seems bad as SimpleDateFormat is not thread safe. However we only access the formatter object in a very small location, so we could change: if (tz != null) { formatter.setTimeZone(tz); } formatted = formatter.format(value); to synchronized(formatter) { if (tz != null) { formatter.setTimeZone(tz); } formatted = formatter.format(value); } The current code does also access formatter when creating, but that's because it's doing daft things in creation. The code below gets rid of that. So this is fixable and needs a demonstrative test to be created.
As with 17700, the question of what to do with the cache over time exists. In this case it seems unlikely that we would have an enormous number of patterns being created; so I wouldn't expect the map to grow hugely. One solution might be to implement a very basic pool - but that seems very likely to lead to bugs while we get it right.
SPI style plugin cache added for this (see attachment to issue https://issues.apache.org/bugzilla/show_bug.cgi?id=31789 ).
Created attachment 20900 [details] Fix+Test patch Attaching a fix and a test. The test fails because Cactus does not seem to set the locale on the request. The default action then is to print date.toString(). Switching that to use the Locale.getDefault() shows that the code is working. Probably have to just ditch the test and just go with the fix, along with a sample app showing that things are okay.
Created attachment 20987 [details] Fix+More Tests Adding a few more tests, though it is more of a proof and not a test.
svn ci -m "Applying the fix and the tests from #32311 - LRUMap used as a caching system to improve on performance" Sending src/org/apache/taglibs/standard/tag/common/fmt/FormatDateSupport.java Adding test/org/apache/taglibs/standard/tag/el/fmt Adding test/org/apache/taglibs/standard/tag/el/fmt/TestDateTag.java Adding test/web/org/apache/taglibs/standard/tag/el/fmt Adding test/web/org/apache/taglibs/standard/tag/el/fmt/TestDateTag.jsp Transmitting file data ... Committed revision 585044.