【问题标题】:JodaTime Chronology cachingJodaTime 年表缓存
【发布时间】:2011-09-21 11:29:23
【问题描述】:

我基于 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 的子类),但我承认我的一些面向对象设计的东西有点生疏(过去两年一直在使用 RPG ).
所以...想法?

【问题讨论】:

    标签: java caching static jodatime


    【解决方案1】:

    关于正确性,通过将 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?
    话虽如此,反之亦然——如果你所拥有的是工作,那么就让它保持原样吧!

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-11
        • 1970-01-01
        • 2011-09-23
        • 2021-11-27
        • 1970-01-01
        • 2011-05-05
        相关资源
        最近更新 更多