Calendar.getInstance()的坑


Calendar.getInstance()看起来应该是个单例,但实际上并不是。
一次在JProfile中查看CPU的消耗的时候,发现 Calendar.getInstance() 消耗的CPU占比比较大,具体看了下代码才发现实际上是每次都创建对象的。



public static Calendar getInstance(TimeZone zone,
                                       Locale aLocale)
    {
        return createCalendar(zone, aLocale);
    }

private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }



代码中可以清楚的看到是每次都创建的。
那么创建对象的成本怎么样呢?

public static void main(String[] args) {
        int runs = 10000;
        long start = System.nanoTime();
        Calendar cal = null;
        for(int i=0;i<runs;i++)
            cal = Calendar.getInstance();
        long time = System.nanoTime() - start;
        System.out.println("Calendar.getInstance() took on average "+time/runs+" ns. "+cal);

        long start2 = System.nanoTime();
        long now = 0;
        for(int i=0;i<runs;i++)
            now = System.currentTimeMillis();
        long time2 = System.nanoTime() - start2;
        System.out.println("System.currentTimeMillis() took on average "+time2/runs+" ns. "+now);
    }


测试结果:
Calendar.getInstance() took on average 8264 ns. java.util.GregorianCalendar[time=1481014104709,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Chongqing",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2016,MONTH=11,WEEK_OF_YEAR=50,WEEK_OF_MONTH=2,DAY_OF_MONTH=6,DAY_OF_YEAR=341,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=48,SECOND=24,MILLISECOND=709,ZONE_OFFSET=28800000,DST_OFFSET=0]
System.currentTimeMillis() took on average 105 ns. 1481014104711

可以看到是System.currentTimeMillis()的80多倍。性能其实是很差了的。
建议Calendar对象可以缓存起来,不用每次都创建。
具体的讨论在stackoverflow上也有: http://stackoverflow.com/questions/4587878/creating-java-object-general-question





你可能感兴趣的:(Calendar.getInstance()的坑)