JDK-5073554 : java.util.Date is a performance bottleneck
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:i18n
  • Affected Version: 1.4.1
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: generic
  • CPU: generic
  • Submitted: 2004-07-13
  • Updated: 2006-06-26
  • Resolved: 2006-06-26
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Name: gm110360			Date: 07/13/2004


java.sql.Date is a performance bottleneck, especially in large multi-threaded middle-tier applications. All of the accessor methods reference a single instance of Calendar. As a result all threads that access Dates serialize on that Calendar.

Yes, the Date accessors, getDay, getMonth, getYear, etc. are all deprecated, but the reality is that is what customer applications use. The recommended method of getting this information is simply unwieldy and customer applications do not use it. Compare

  int year = myDate.getYear() + 1900;

with

  int year;
  synchronized (myCalendar) {
    myCalendar.setTime(myDate);
    year = myCalendar.get(Calendar.YEAR);
  }

The synchronized is necessary for thread safety.

The above code clearly demonstrates the problem. The implementation of Date.getYear is more or less the recommended code above. The Calendar is stored in a static so every thread is competing for access to that one instance.

What is worse, applications typically call getYear, getMonth, getDay in sequence. Each call results in calling setTime again and most likely recomputing the date attributes because some other thread got access to the Calendar object between calls to the Date accessors and thus called setTime with a different Date. This results in multiple computations of the same date attributes. Oracle Applications has seen up to 11% of CPU time spent inside Date accessors in tests. As a result the production version of Oracle Applications avoids using java.sql.Date as much as possible.

You may wish to argue that this is not a bug as the methods in question are deprecated. That ignores the plain fact that the suggested code is unreasonable and that customers generally do not use it. One of the major thrusts of JSR221 (JDBC 4.0) is ease of development. Requiring that customers use the unwieldy recommended code instead of the simple and obvious deprecated code is strongly counter to ease of development. And it is not necessary.

java.sql.Date (actually the superclass java.util.Date) can be implemented in at least two other ways that would vastly improve performance. The simplest change would be to store a Calendar object in a thread local variable rather than a static. This would eliminate the thread contention.

An even better approach would be to recognize that the deprecated Date methods are the best way to access date attributes and recode Date accordingly. Here is one possible approach. There are many others.

Add an int array field to Date

  int[] attributes;

and a method

  public int[] getDateAttributes(Date d)

to Calendar. This method is not synchronized. Then getYear could be coded as

  public int getYear() {
    if (attributes == null)
      attributes = myCalendar.getDateAttributes(this);
    return attributes[myCalendar.yearIndex];
  }

Note that there is no synchronization. Yes, occasionally attributes will be set more than once, but it will always be set to identical values so this is not a problem and is much better than synchronizing. This implementation allows Calendar subclasses to compute different attribute values based on the calendar they support while still supporting a generic implementation in java.util.Date.

A further issue is that the computations inside GregorianCalendar could be optimized. The overwhelming majority of Dates are between 1901 and 2099. This is a special case since 2000 is a leap year so every fouth year between 1901 and 2099 is a leap year, there is no issue of Julian year, etc. Two compares of the millisecond value (or year value) would identify the special case and permit the use of highly optimized code for the overwhelming majority of dates. As things stand, the fully general algorithm is used for every date greatly reducing performance in real world apps.
(Incident Review ID: 280077) 
======================================================================

Comments
EVALUATION The performance problem has been resolved by several fixes. Please refer to the See Also bug reports. Closing this one as "not reproducible".
26-06-2006

WORK AROUND Name: gm110360 Date: 07/13/2004 Do not use java.sql.Date. Instead fetch DATE values as Strings or some other type. ======================================================================
08-09-2004

EVALUATION The static GregorianCalendar instances were eliminated as a part of the 4614842 fixes. Also the synchronization issue of the default TimeZone instance was fixed as 4692504. Please evaluate those fixes to see if the Tiger Date class performance is acceptable. If not, we do need a new date/time API which was however rejected for Tiger. ###@###.### 2004-07-16
16-07-2004