JodaTime年表缓存

时间:2011-06-23 18:25:10

标签: java caching static jodatime

我已经成功地编写了一份新的年代表,代表了我公司的财务日历,基于JodaTime。我简单地提到了JodaTime源代码,弄清楚我需要做什么。我在BasicChronology类中注意到的一件事是使用内部类YearInfo来缓存'firstDayOfYearMillis' - 自1970-01-01(ISO)以来的毫秒数。想象一下,如果JodaTime缓存它的性能瓶颈就足够了,我应该把它添加到我的年表中。
但是,当我这样做时,我做了一些修改。具体来说,我将getYearInfo方法移动到YearInfo内部类中,并将其设置为静态。我还将用于存储缓存值的数组移动到内部类中。修改后的类的完整定义如下:

/**
 * Caching class for first-day-of-year millis.
 *
 */
private static final class YearInfo {

    /**
     * Cache setup for first-day-of-year milliseconds.
     */
    private static final int CACHE_SIZE = 1 << 10;
    private static final int CACHE_MASK = CACHE_SIZE - 1;
    private static transient final YearInfo[] YEAR_INFO_CACHE = new YearInfo[CACHE_SIZE];

    /**
     * Storage variables for cache.
     */
    private final int year;
    private final long firstDayMillis;
    private final boolean isLeapYear;


    /**
     * Create the stored year information.
     * 
     * @param inYear The year to store info about.
     */
    private YearInfo(final int inYear) {
        this.firstDayMillis = calculateFirstDayOfYearMillis(inYear);
        this.isLeapYear = calculateLeapYear(inYear);
        this.year = inYear;
    }

    /**
     * Get year information.
     * 
     * @param year The given year.
     * 
     * @return Year information.
     */
    private static YearInfo getYearInfo(final int year) {
        YearInfo info = YEAR_INFO_CACHE[year & CACHE_MASK];
        if (info == null || info.year != year) {
            info = new YearInfo(year);
            YEAR_INFO_CACHE[year & CACHE_MASK] = info;
        }
        return info;
    }
}

我的问题是......我的更改有哪些性能或设计含义?我已经决定我的更改应该是线程安全的(给出关于最终成员变量的答案)。但是为什么最初的实现按原样执行,而不是这样?我明白为什么大多数静态有效使用的方法都没有(给定BasicChronology的子类),但我承认我的一些OO设计有点生疏(过去两年都花了)使用RPG) 那么......想法?

2 个答案:

答案 0 :(得分:2)

关于正确性,通过将YEAR_INFO_CACHE切换为静态,您引入了次要内存泄漏。有几种方法可以判断您的静态参考在实践中是否重要,例如:根据您对数据的了解,对缓存增长的大小进行背面近似估算;在应用程序的负载测试期间/之后对堆进行概要分析;等

你正在缓存这样的小对象,你可能可以毫无问题地缓存它们。尽管如此,如果您发现缓存需要受限,那么您有一些选项,例如LRU缓存,基于软引用的缓存而不是直接(强)引用等。但是,我再次强调,特殊情况,实施其中任何一个might be a waste of time

为了解释静态引用的理论问题,我将参考其他帖子,而不是在这里复制它们:
 1. Are static fields open for garbage collection?
 2. Can using too many static variables cause a memory leak in Java?

同样关于正确性,代码是线程安全的,不是因为引用是最终的,而是因为多个线程为某个缓存位置创建的YearInfo值必须相等,所以哪个最终在缓存中都无关紧要。

关于设计,原始Joda代码中的所有YearInfo相关内容都是私有的,因此包含缓存的YearInfo详细信息被很好地封装。这是一件好事。

关于性能,最好的办法是分析代码并查看使用大量CPU的内容。对于分析,您希望查看在此代码中花费的时间是否与整个应用程序的上下文相关。在加载下运行您的应用程序,并检查代码的这一特定部分是否重要。如果即使没有YearInfo缓存也没有在此代码中看到性能问题,那么可能没有充分利用时间来处理/担心缓存。以下是有关如何进行检查的一些信息:
 1. Performance profiler for a java application
 2. How to find CPU-intensive class in Java?
也就是说,反过来也是如此 - 如果你有什么工作,那么保持原样!

答案 1 :(得分:2)

我编写了缓存到YearInfo对象中的原始代码。您将更多逻辑封装到YearInfo类中的解决方案非常好,并且应该也可以执行。我根据意图设计了YearInfo - 我想要一个粗略的数据对,仅此而已。如果Java支持结构,我会在这里使用它。

至于缓存设计本身,它是基于分析结果来查看它是否有任何影响。在大多数地方,Joda-Time懒惰地计算字段值,并且稍后缓存它们确实提高了性能。因为这个特定的缓存大小是固定的,所以它不会泄漏内存。它消耗的最大内存量是1024个YearInfo对象,大约是20k字节。

Joda-Time充满了这样的专业缓存,并且所有这些缓存都显示出可衡量的性能提升。我不能说这些技术是多么有效,因为它们是针对JDK 1.3编写和测试的。