-
Bug
-
Resolution: Cannot Reproduce
-
P4
-
None
-
1.4.1
-
generic
-
generic
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)
======================================================================
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)
======================================================================
- relates to
-
JDK-4692504 TimeZone.getDefault() has too much synchronization
- Resolved
-
JDK-6271513 (tz) TimeZone.getDisplayName should use ConcurrentMap for caching display names
- Resolved
-
JDK-4614842 Performance improvements to GregorianCalendar and Date
- Resolved
-
JDK-5079972 (tz) Eliminate synchronization from TimeZone.getDefault
- Closed
-
JDK-4340168 (cal) RFE: Replace Calendar and GregorianCalendar
- Closed