【问题标题】:Use PermGen space or roll-my-own intern method?使用 PermGen 空间或 roll-my-own 实习生方法?
【发布时间】:2011-02-21 08:14:00
【问题描述】:

我正在编写一个编解码器来处理使用定制有线协议通过 TCP 发送的消息。在解码过程中,我创建了许多Strings、BigDecimals 和日期。客户端-服务器访问模式意味着客户端发出请求然后解码数千条响应消息是很常见的,这会导致大量重复Strings、BigDecimals等。

因此,我创建了一个 InternPool<T> 类,允许我实习每个类的对象。在内部,池使用WeakHashMap<T, WeakReference<T>>。例如:

InternPool<BigDecimal> pool = new InternPool<BigDecimal>();

...

// Read BigDecimal from in buffer and then intern.
BigDecimal quantity = pool.intern(readBigDecimal(in));

我的问题:我将InternPool 用于BigDecimal,但我是否应该考虑将它也用于String 而不是 Stringintern() 方法,我相信它使用永久代空间?使用 PermGen 空间有什么好处?

【问题讨论】:

  • @kts:如果我要将 byte[] 映射到 BigDecimal,问题是一旦实习池创建/返回 BigDecimal,任何东西都不会引用 byte[]。假设 byte[] 是底层 WeakHashMap 中的键,这将导致条目被删除,尽管相应的 BigDecimal 正在使用中。
  • WeakReference 适合这个,还是您应该使用SoftReference? GC 对两者的行为不同,这听起来像是您正在尝试创建一种缓存;弱引用不适用于此目的。出于某些原因,请在此处查看我的答案:stackoverflow.com/questions/2861410/…
  • @Adamski 我将仅在 BigDecimal 上使用 SoftReference,并在 BigDecimal 入队后使用 ReferenceQueue 从地图中删除 byte[]s。 (可能需要一个 BiMap)。这可以消除冗余 BigDecimal 对象的构造,从而节省内存/gc 运行时间和执行时间(只需构造一次)。
  • 仔细考虑,实习生 byte[] 可能是一个更好的主意,并且只有在您真正需要使用它时才转换为 BigDecimal。这个操作也可以被缓存。这提供了 byte[] b = pool.intern(getBytes()); 的简单性和惰性构造的好处。在这两种情况下,您都必须读取字节(或任何您使用的 BigDecimal),但在这种情况下,您只会读取每个唯一 BigDecimal 的 ctor 1。
  • @Adamski:你不能决定“使用 PermGen” [原文如此]。 String 的 intern 机制与 String“池化”有关,但不一定与 PermGen 内存池有关。 PermGen 是特定于 Sun VM 的功能,在语言/VM 规范级别不存在。

标签: java garbage-collection permgen string-interning


【解决方案1】:

如果你已经有这样一个InternPool 类,它认为使用它比为字符串选择不同的实习方法更好。特别是因为String.intern() 似乎提供了比您实际需要的更强大的保证。您的目标是减少内存使用量,因此实际上不需要在 JVM 的生命周期内进行完美的实习。

另外,我会使用 Google Collections MapMaker 创建一个 InternPool 以避免重新创建轮子:

Map<BigDecimal,BigDecimal> bigDecimalPool = new MapMaker()
    .weakKeys()
    .weakValues()
    .expiration(1, TimeUnits.MINUTES)
    .makeComputingMap(
      new Function<BigDecimal, BigDecimal>() {
        public BigDecimal apply(BigDecimal value) {
          return value;
        }
      });

这会给你(正确实现的)弱键和值、线程安全、旧条目的自动清除和一个非常简单的界面(一个简单的、众所周知的Map)。可以肯定的是,您还可以使用 Collections.immutableMap() 将其包装起来,以避免错误的代码弄乱它。

【讨论】:

  • 好的,谢谢。 String.intern() 是否在 JVM 的生命周期内实习?我不确定这是不是真的,因为我认为现代 VM 垃圾是从 PermGen 收集的。
  • @Joachim - 你似乎在暗示一个实习字符串将在 JVM 的生命周期内存活。 javadocs 不能保证这一点,事实上,我认为最近的 JVM 并非如此。
  • @Stephen:我试过 not 来暗示这一点,因为 JavaDoc 确实没有说明这一点。
  • @Joachim - 在那种情况下,我不明白你所说的 是什么意思......似乎提供了比你实际需要的更有力的保证”。跨度>
  • ...另外,Guava 现在有Interners,所以我不确定MapMaker 方法是否还有必要。
【解决方案2】:

JVM 的String.intern() 池可能会更快。 AFAIK,它是在本机代码中实现的,因此它应该比使用 WeakHashMapWeakReference 实现的池更快并且使用更少的空间。您需要进行一些仔细的基准测试来确认这一点。

但是,除非您有大量长期存在的重复对象,否则我怀疑实习(在 permGen 中或在您自己的池中)会产生很大的不同。如果唯一对象与重复对象的比例太低,那么实习只会增加活动对象的数量(使 GC 花费更长的时间)并由于实习的开销而降低性能等等。因此,我还主张对“实习生”与“非实习生”方法进行基准测试。

【讨论】:

  • Adamski 确实有大量长期存在的重复对象 :-)
  • @oxbow_lakes - 非常聪明。关键是您需要量化这些事情,以确定实习(通过任何机制)是否会提高性能......还是让它变得更糟。影响结果的因素有很多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-23
  • 1970-01-01
  • 2011-09-04
相关资源
最近更新 更多