2011-06-23 57 views
2

我已经成功地编写了一个新的代表我公司财政日历的年表,基于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

关于正确性,通过将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缓存,您在代码中也没有看到性能问题,那么可能不是一个很好的时间来处理/担心缓存。下面是关于如何做好查了一些资料:

2. How to find CPU-intensive class in Java?
这就是说,反过来也是如此 - 如果你有工作的,那么保留原样!

2

我写了缓存到YearInfo对象中的原始代码。将更多逻辑封装到YearInfo类中的解决方案非常完美,并且应该表现得同样出色。我根据意图设计了YearInfo - 我想要一个粗略的数据对,而不是更多。如果Java支持的结构,我会在这里使用一个。

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

乔达时间充满了这样的专门缓存,所有这些都显示出可衡量的性能提升。我不能说这些技术有多有效,因为它们是针对JDK 1.3编写和测试的。