【问题标题】:Garbage collector and String.intern()垃圾收集器和 String.intern()
【发布时间】:2015-01-06 11:17:58
【问题描述】:

如果我这样做

StringBuilder b = new StringBuilder();
b.append("foo").append("bar");
String s = b.toString().intern();
s = null;

StringBuilder 创建的 "foobar" 字符串是否可用于垃圾收集器?

我怀疑intern() 方法,因为我正在将应用程序的一些代码块与基于字符串的ID 同步。

类似这样的:

String id = getUserCurrentId() // this method generates IDs with StringBuider...
synchronized(id.intern()){
    // .....
}

这是一个桌面应用程序,有一些线程,每个登录的用户在每个登录过程都拥有一个生成的 ID。

【问题讨论】:

  • 团队决定不对源代码进行任何更改。尽管 JVM 在.intern() 之后维护了每个字符串的副本,但测试表明内存使用率并不高。运行时生成的所有其他副本都正常进行垃圾回收。

标签: java string garbage-collection


【解决方案1】:

我认为它会起作用:

虽然even interned Strings can be garbage collected(这样您可能会在 JVM 的生命周期内获得不同的用户 ID 实例),但当您在该同步块内时,它们不会被垃圾收集(因为字符串仍在使用),所以它应该足够稳定以满足您的目的。

即使下次你得到另一个实例,那也只能意味着锁是没有争议的,所以没关系。

但我还是不会这样做。

a ConcurrentHashMap to get named locks怎么样?

【讨论】:

    【解决方案2】:

    当您调用str.intern() 时,您确实可以访问字符串的内部实例,即如果该字符串已经在缓存中,您将获得对该实例的引用。

    当你说:

    String s = .... /* anything */
    

    您创建的变量包含对同一字符串的另一个引用。 所以当你说

    s = null; 
    

    您只需将 null 放在该引用中即可。它不会影响对象本身。因此,如果 GC 决定删除它,它将被 GC 删除。您仍然可以使用源对象(即写入赋值运算符的权利)进行同步。当然synchronized(id.intern()) 看起来不错。虽然我不知道你为什么要这样做。

    顺便说一句,在您的程序中重用具有功能意义的对象进行同步是一种非常糟糕的模式。考虑以下场景。您正在使用 id.intern() 进行同步。这意味着如果您的 ID 是例如 foo 并且程序其他部分的某个人说

    String s = 'foo'
    

    他可以访问同一个对象,因为字符串文字被缓存了。 现在如果在程序的其他部分编写以下代码:

    String s = 'foo';
    .....
    synchronized(s) {
        s.notify();
    }
    

    在你写的部分代码

    synchronized(id.intern()) {
        id.wait();
    }
    

    您的wait() 可能会退出!程序的这种意外行为很难调试。因此,更好的做法是使用特殊对象进行锁定。

    【讨论】:

    • +1 表示“为锁使用特殊(和私有)对象”
    猜你喜欢
    • 1970-01-01
    • 2015-03-14
    • 2018-12-30
    • 1970-01-01
    • 2011-01-06
    • 2015-07-10
    • 2010-12-23
    • 2015-11-26
    • 2013-01-17
    相关资源
    最近更新 更多