【问题标题】:Storing property value names as String constants - performance and memory usage?将属性值名称存储为字符串常量 - 性能和内存使用情况?
【发布时间】:2010-08-01 12:13:26
【问题描述】:

我使用了大约 1000 个与特定 java.util.Properties 相关联的属性,这些属性由文件支持。该文件的主要原因是在不重新编译程序的情况下更改它们,并允许用户根据自己的喜好进行调整。 有些属性只在代码中的一处使用,但也有一些属性在不同的代码段甚至不同的类中使用了多次。

我最近养成了声明所有用作字符串常量的属性的习惯,通常在这样的单独接口中:

public interface AnimalConstants {
  public static final String WEIGHT_PROPERTY = "weight";
  public static final String RUNNING_SPEED_PROPERTY = "speedInKph";
  public static final String HOURS_OF_SLEEP_A_DAY_PROPERTY = "sleepHrs";
  ...
}

当一个类需要访问一些动物属性时,我只需实现这个接口,就可以访问所有声明的属性常量。当我需要一个特定的属性时,我只使用相应的常量,而不考虑它的确切名称(因为经常使用缩写),更重要的是,通过这种方式消除了错误输入属性名称的风险。另一个优点是,如果我稍后选择重命名属性以使配置这些属性的高级用户更清楚),我只需要在声明该属性常量的界面中更改该名称(当然还有属性文件),因此无需“搜索和替换”整个项目。最后,我可以轻松检查该属性是否正在使用;我只是评论一下,编译代码,看看有没有错误。

然而,尽管有所有这些优点,但我很好奇这种方法的缺点是什么。我最感兴趣的是以下几点:

  • 这种方法(1000 个字符串常量)对字符串池有什么影响?还是在我访问这些常量时按需创建它们?这是否会阻止其他字符串缓存在字符串池中?
  • 与我使用硬编码字符串常量的方法相比,这种方法的性能成本是多少,是否相同(忽略访问字段的成本)?字符串池的行为是相似还是有很大不同?
  • 这种方法平均增加了多少内存,所有这些字符串常量是否一直保存在内存中?

欢迎任何好的评论/观察。

【问题讨论】:

  • 我真的看不出这比让你的代码到处都是文字的性能要差。
  • 哦,不要再使用常量接口了。创建接口不是为了用作常量持有者,它们是为合同而创建的。您可以使用类来保存常量。
  • 是的,接口不应该用于常量。将他们归入自己的班级;然后,您可以使用静态导入来导入它们,这样您在使用它们时就不需要限定它们。
  • 所以如果我只使用类而不是接口并没有真正的缺点,对吧?
  • 我建议,更好的方法是将属性名称放入 Java enum。这样您就不会意外使用不相关的字符串作为属性名称。 Enum 的内存占用会比直接 String 常量稍高,但是,嘿,现在不是 1974 年,而且内存很便宜。另一方面,维护和调试与以往一样昂贵。

标签: java performance string properties constants


【解决方案1】:

静态字段在类加载期间的初始化阶段被初始化。

但是,如果将原始类型或字符串定义为常量并且值在编译时已知,则编译器会用其值替换代码中各处的常量名称。这称为编译时常量。如果外界常量的值发生变化(例如,如果立法规定 pi 实际上应该是 3.975),您将需要重新编译任何使用该常量的类来获取当前值。 这是将创建唯一字符串的字符串文字的时间,这些文字被定义为常量的值。

但它类似于从 Properties 对象中的资源加载常量(为此编写代码)。常量肯定会消耗内存。 字符串池行为不会改变。

对设计方法的一些思考:

很容易将所有配置常量放在一个类中,然后在整个应用程序中引用它们。 要更改静态配置常量,您需要修改源代码并重新编译。 如果在将来的某个时候,您的程序需要维护多个配置,或者在处理不同文件时在配置之间交替,或者甚至运行具有一种配置的一个线程和具有不同配置的另一个线程,该怎么办。您将无法使用该技术。 因此,为了更好的设计,您将永远不会更改的常量存储为类定义中的静态常量。这些在类加载到内存时立即加载。 在我之前描述的其他情况下(资源加载),您将它们保存在可以加载到 JAVA 属性对象中的各种属性文件中。此类情况的示例可以是 JDBC 连接相关信息等...

【讨论】:

  • 你的意思是我只需要将接口更改为类就可以了吗?我不明白你所说的“改变不同的配置”是什么意思,如果你能解释一下或举一个简短的例子吗?
  • 可能存在配置因部署而异的情况。 DB 是一个很好的例子。每个部署的数据库服务器可以是不同的类型(oracle、sqlserver)以及基本细节可以改变,如服务器 ip、用户、密码等。
  • @Yok 请解释一下“类中定义的静态常量在类被 JVM 加载后立即加载”是什么意思。
  • @naikus 编辑并更新了我的答案,添加了更多详细信息。
  • @Yok 我认为第一句话具有误导性。应该是:静态字段在类加载的初始化阶段被初始化。
【解决方案2】:

1) 这种方法(1000 个字符串常量)对字符串池有什么影响?

将属性名称声明为字符串常量是正确的决定。但是“字符串文字池”不会有任何变化。当存在同一个字面量的多个实例时,它们都只是简单地链接到字面量池中的同一个项目(前提是不使用 String 构造函数)。

2) 它们是在我访问这些常量时按需创建的?

在类加载期间将字符串文字添加到“字符串文字池”中。

3) 这是否会阻止其他字符串缓存在字符串池中?

没有。

4) 与我使用硬编码字符串常量的方法相比,这种方法的性能成本是多少,是否相同(忽略访问字段的成本)?字符串池的行为是相似还是有很大不同?

是一样的。

5) 这种方法的平均内存增加是多少,所有这些字符串常量是否一直保存在内存中?

我已经回答过了。 :)

补充说明

a) Constants interface 是 Java 反模式。请避免。

b) 你必须阅读这个Javaranch article。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-05
    • 1970-01-01
    • 2012-09-21
    • 2011-08-09
    • 2014-09-04
    • 1970-01-01
    • 2014-02-08
    相关资源
    最近更新 更多