Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用java.lang.String.intern()是否是一种良好的做法?_Java_String - Fatal编程技术网

使用java.lang.String.intern()是否是一种良好的做法?

使用java.lang.String.intern()是否是一种良好的做法?,java,string,Java,String,Javadoc没有给出太多细节。简而言之:它返回字符串的规范表示形式,允许使用== 我什么时候会使用这个函数来支持String.equals? Javadoc中是否有未提及的副作用,即JIT编译器或多或少的优化? String.intern还有其他用途吗? 我什么时候会使用这个函数来支持String.equals 当您需要速度时,因为您可以通过引用比较字符串==比equals快 Javadoc中是否没有提到副作用 主要的缺点是,您必须记住,确保您实际实习了所有要比较的字符串。很容易忘记对所有字符

Javadoc没有给出太多细节。简而言之:它返回字符串的规范表示形式,允许使用==

我什么时候会使用这个函数来支持String.equals? Javadoc中是否有未提及的副作用,即JIT编译器或多或少的优化? String.intern还有其他用途吗? 我什么时候会使用这个函数来支持String.equals

当您需要速度时,因为您可以通过引用比较字符串==比equals快

Javadoc中是否没有提到副作用

主要的缺点是,您必须记住,确保您实际实习了所有要比较的字符串。很容易忘记对所有字符串进行实习,然后可能会得到令人困惑的错误结果。另外,为了每个人的利益,请确保非常清楚地记录您所依赖的字符串是内部化的

如果决定内部化字符串,第二个缺点是intern方法相对昂贵。它必须管理唯一字符串池,这样即使字符串已经内部化,它也会做相当多的工作。所以,在你的代码设计中要小心,例如,你可以在输入中插入所有合适的字符串,这样你就不必再担心它了

来自JGuru

第三个缺点仅限于Java7或更低版本:插入的字符串位于PermGen空间中,该空间通常非常小;您可能会遇到OutOfMemoryError,其中有大量可用堆空间


迈克尔·博格沃德(Michael Borgwardt)

说,我不知道有什么好处,如果有好处的话,人们会认为equals本身会在内部使用intern,而实际上并没有


这几乎与字符串比较无关。用于在应用程序中有多个具有相同内容的字符串时节省内存。通过使用String.intern,从长远来看,应用程序将只有一个实例,其副作用是,您可以执行快速引用相等比较,而不是普通的字符串比较,但这通常是不可取的,因为如果忘记只插入一个实例,就很容易被破坏。

我将检查intern和==-仅当相等比较成为字符串多重比较的瓶颈时,才进行比较而不是相等。这不太可能有助于进行少量的比较,因为实习生不是免费的。在积极地实习字符串之后,你会发现对实习的呼叫越来越慢

我什么时候会使用这个函数来支持String.equals

考虑到他们做不同的事情,可能永远不会

出于性能原因而保留字符串,以便您可以比较它们的引用相等性,只有在您保留字符串引用一段时间的情况下才有好处-来自用户输入或IO的字符串不会被保留

这意味着在您的应用程序中,您接收来自外部源的输入,并将其处理成一个具有语义值(比如标识符)的对象,但该对象的类型与原始数据不可区分,并且对于程序员应该如何使用该对象有不同的规则

创建一个用户ID类型几乎总是比创建一个线程安全的通用内部机制和一个开放的枚举要好的多,如果java.lang.String类型恰好是一个用户ID,那么创建一个线程安全的通用内部机制和类似于一个开放的枚举的行为要容易得多


这样,您就不会混淆某个特定字符串是否已被插入,并且可以在开放枚举中封装所需的任何其他行为。

我认为它不值得维护

大多数情况下,没有必要,也没有性能优势,除非您的代码对子字符串做了大量工作。在这种情况下,String类将使用原始字符串加上偏移量来节省内存。如果您的代码大量使用子字符串,那么我怀疑这只会导致内存需求爆炸。

String.intern在现代JVM中肯定是垃圾收集的。 由于GC活动,以下内容永远不会耗尽内存:

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

更多信息请参见。

使用intern的真正原因不是上述原因。 您可以在内存不足错误后使用它。在一个典型的程序中,很多字符串都是其他大字符串的string.substring[想想从一个100K xml文件中取出一个用户名。 java实现是这样的,子字符串包含对原始字符串的引用和该大字符串中的start+end,其背后的思想是重用相同的大字符串

在1000个大文件之后,您只保存1000个短名称,您将在内存中保留全部1000个文件!
解决方案:在这种情况下,只需使用smallsubstring.intern

我使用intern来节省内存,我在内存中保存了大量字符串数据,并移动到使用intern来节省大量内存 记忆。不幸的是,尽管它使用的内存很少,但它使用的内存存储在PermGen内存中,而不是堆中,因此很难向客户解释如何增加这种类型内存的分配

那么,有没有替代intern的方法来减少内存消耗呢?==与equals的性能优势对我来说不是问题

比较字符串与==比使用equals快得多

5时间快,但由于字符串比较通常只代表应用程序总执行时间的一小部分,因此总体增益远小于此值,最终增益将稀释到几个百分点

intern将字符串从堆中拉出并放入PermGen

内部化的字符串放在不同的存储区域中:这是JVM的一个区域,保留给非用户对象,如类、方法和其他内部JVM对象。这个区域的大小是有限的,而且比堆要珍贵得多。由于该区域比堆小,因此更有可能使用所有空间并获得OutOfMemoryException

已对String.intern字符串进行垃圾收集

在JVM的新版本中,当没有被任何对象引用时,内部化的字符串也会被垃圾收集

记住上面的3点,你可以推断String intern只在你做大量字符串比较的少数情况下有用,但是如果你不知道你在做什么,最好不要使用内部字符串

断言String.equals使用==来比较之前的字符串对象,根据

它比较字符串的长度,然后比较内容

顺便说一句,销售目录中的产品代码字符串可能都是相同的长度——BIC0417是bicycist的安全帽,TIG0003是活的成年雄性老虎- 您可能需要各种许可证才能订购其中一种。也许你最好同时订购一顶安全帽

因此,听起来用实习生版本替换字符串似乎有好处,但在编程中不使用==for equals,就可以获得安全性、可读性和标准遵从性。我要说的大部分取决于这是真的,如果是真的话

但是String.equals是否在使用==之前测试您是否向其传递了一个字符串而不是其他对象?我没有资格这么说,但我想不会,因为绝大多数这样的equals操作都是字符串对字符串的,所以测试几乎总是通过的。实际上,在String.equals中优先排序==意味着您经常将字符串与同一实际对象进行比较

我希望没有人会对以下几行产生的错误结果感到惊讶:

    Integer i = 1;
    System.out.println("1".equals(i));
但是如果你把第二行的i改成i.toString,那当然是真的

显然,你希望从实习中获益的场所包括Set和Map。我希望实习的字符串缓存了它们的哈希代码。。。我认为这是一个要求。我希望我没有放弃一个能让我赚一百万美元的想法-

至于内存,很明显,如果字符串的数量很大,或者希望程序代码使用的内存非常小,那么这是一个重要的限制。如果您的不同字符串的容量非常大,那么就可以考虑使用专用的数据库程序代码来管理它们,以及单独的数据库服务器。同样,如果您可以改进一个需要同时在10000个实例中运行的小程序,让它完全不存储字符串本身

创建一个新字符串,然后直接丢弃它作为实习生替代品,这感觉很浪费,但是除了保留重复的字符串外,没有明确的替代方法。因此,执行成本实际上是在实习生池中搜索字符串,然后允许垃圾收集器处理原始字符串。如果它是一个字符串文字,那么它已经实习了


我想知道intern是否会被恶意程序代码滥用,以检测intern池中是否已经存在某些字符串及其对象引用,从而存在于Java会话中的其他位置,而这是不应该知道的。但我想,只有当程序代码已经被可靠地使用时,这才有可能。不过,在你的程序中包括第三方库来存储和记住ATM PIN号是一件值得考虑的事情。p> 当结果与源字符串相比很小且对象的寿命较长时,使用子字符串可能会导致内存泄漏

通常的解决方案是使用新字符串s.subString。。。但是当您有一个存储潜在/可能的子字符串结果的类时。。。对调用方没有控制权,您可以考虑将传递给构造函数的字符串参数的实习生存储起来。这释放了潜在的巨大压力
e buffer.

让我们面对它:主要的用例场景是,当您通过输入流或JDBC结果集读取数据流时,有无数的小字符串在整个过程中重复

下面是一个小技巧,让您可以控制希望使用何种机制来内部化字符串和其他不可变项,以及一个示例实现:

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

丹尼尔·布鲁克纳是绝对正确的。字符串插入是为了节省内存堆。我们的系统目前有一个巨大的hashmap来保存某些数据。随着系统的扩展,hashmap将变得足够大,可以像我们测试的那样使用内存堆。通过将所有重复的字符串和hashmap中的所有对象放在一起,它为我们节省了大量的堆空间

因此,您不必担心它的大小,而且它会被垃圾收集:

在JDK 7中,不再在永久数据库中分配内部字符串 生成Java堆,但在主 Java堆的一部分称为年轻人和老年人,以及 使用应用程序创建的其他对象。这一变化将 导致更多的数据驻留在主Java堆中,而更少的数据驻留在主Java堆中 永久性生成,因此可能需要 调整。大多数应用程序只会看到相对较小的差异 由于此更改,堆使用率降低,但更大的应用程序 许多类或大量使用String.intern方法将看到 更显著的差异


我使用它是为了缓存链接到相关名称的大约36000个代码的内容。我在缓存中插入字符串,因为许多代码指向同一个字符串

通过在缓存中插入字符串,我可以确保指向同一字符串的代码实际上指向同一内存,从而节省RAM空间


如果被截取的字符串实际上是垃圾收集的,那对我来说根本不起作用。这基本上否定了实习的目的。我的不会被垃圾收集,因为我持有对缓存中每个字符串的引用。

我最近写了一篇关于string.intern在Java 6、7和8中实现的文章:

我希望它应该包含关于Java中字符串池的现状的足够信息

简言之:

避免使用Java 6中的String.intern,因为它会进入PermGen 在Java 7和Java 8中更喜欢String.intern:它使用的内存比滚动自己的对象池少4-5倍 确保调优-XX:StringTableSize默认值可能太小;设置质数
插入字符串的成本远远超过单个字符串所节省的时间a.equalsB比较。只有在重复使用相同的未更改字符串变量时,才能出于性能原因使用它。例如,如果您定期迭代一个稳定的字符串列表,以更新在同一字符串字段上键入的一些映射,您可以得到一个很好的保存

我建议在优化代码的特定部分时使用字符串实习来调整性能

还要记住字符串是不可变的,不要犯愚蠢的错误

String a = SOME_RANDOM_VALUE
a.intern()
记得做

String a = SOME_RANDOM_VALUE.intern()

在经常调用equals方法的情况下,字符串插入非常有用,因为equals方法会快速检查方法开头的对象是否相同

if (this == anObject) {
    return true;
}
这通常发生在搜索集合时,尽管其他代码也可能执行字符串相等性检查

不过,实习是有成本的,我对一些代码进行了微基准测试,发现实习过程将运行时间提高了10倍

最好的实习地点通常是在读取存储在代码外部的键时,因为代码中的字符串会自动实习。这通常发生在应用程序的初始化阶段,以防止第一个用户受到惩罚

另一个可以执行此操作的地方是在处理可用于进行键查找的用户输入时。这通常发生在请求处理器中,请注意,插入的字符串应该向下传递

除此之外,在代码的其余部分进行实习没有多大意义,因为实习通常不会带来任何好处

Javadoc中是否有未提及的副作用,即JIT编译器或多或少的优化

我不知道JIT级别,但是字符串池有直接的字节码支持,它是用一个专用的常量字符串信息结构神奇而高效地实现的,不像大多数其他对象有更通用的表示

JVM :

字符串文字是对类字符串实例的引用,它源自类或接口二进制表示中的常量字符串信息结构§4.4.3。常量字符串信息结构给出了Unicode代码点的序列 tuting字符串文本

Java编程语言要求相同的字符串文字(即包含相同代码点序列的文字)必须引用类别字符串JLS§3.10.5的相同实例。此外,如果对任何字符串调用String.intern方法,则结果是对同一类实例的引用,如果该字符串显示为文本,则将返回该类实例。因此,以下表达式的值必须为true:

为了派生字符串文字,Java虚拟机检查常量字符串信息结构给出的代码点序列

如果方法String.intern以前曾在包含Unicode码点序列的类String实例上被调用,该序列与常量字符串信息结构给出的序列相同,则字符串文字派生的结果是对该类String实例的引用

否则,将创建一个新的类字符串实例,其中包含常量字符串信息结构给出的Unicode代码点序列;对该类实例的引用是字符串文字派生的结果。最后,调用新字符串实例的intern方法

字节码 查看OpenJDK 7上的字节码实现也很有意义

如果我们反编译:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}
我们在恒定池上有:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc
主要内容:

请注意:

0和3:文本中加载了相同的ldc 2常量 12:以2作为参数创建新的字符串实例 35:a和c作为常规对象与if_acmpne进行比较 常量字符串的表示在字节码上非常神奇:

它有一个专用的结构,不同于常规对象,例如新字符串 结构指向包含数据的。这是表示字符串所需的唯一数据。 上面JVM的引用似乎是说,只要指向的Utf8相同,ldc就会加载相同的实例

我已经对字段进行了类似的测试,并且:

静态最终字符串s=abc通过 非最终字段没有该属性,但仍然可以用ldc初始化
额外好处:与没有直接字节码支持的相比,没有常量字符串信息模拟。

如果您正在寻找字符串.intern的无限替代品,同时也是垃圾收集的,以下内容对我来说很好

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

当然,如果您可以粗略估计将有多少个不同的字符串,那么只需将String.intern与-XX:StringTableSize=highEnoughValue一起使用即可。

第三个缺点:插入的字符串位于PermGen空间中,通常非常小;您可能会遇到OutOfMemoryError,其中有大量可用堆空间。AFAIK较新的虚拟机也会对永久空间进行垃圾收集。Intern是关于内存管理,而不是比较速度。if s1.equals2和if i1==i2之间的差异很小,除非您有许多具有相同前导字符的长字符串。在除URL以外的大多数实际使用中,字符串在前几个字符内会有所不同。长if-else链仍然是一种代码味道:使用枚举和函子映射。您仍然可以在整个程序中使用s1.equals语法,不要使用==,.equals使用==内部短路求值Michael Borgwardt没有说内部字符串不能被垃圾收集。这是一个错误的断言。Michael的评论正确地说的比这更微妙。尽管你说你没有意识到任何优势,但你发布的链接将通过==进行比较识别为快5倍,因此对于以文本为中心的性能代码非常重要。当你有大量文本比较要做时,你最终会耗尽PermGen空间。当没有那么多的文本进行比较时,速度差异并不重要。不管怎样,只是不要实习你的字符串。这不值得。它还说总体相对增益通常会很小。我认为这种逻辑是不正确的。好链接@什么逻辑?这是一个很大的谬误@对象:对不起,你的论点没有理由。使用intern有很好的理由,equals在默认情况下不这么做也有很好的理由。你发布的链接完全是胡说八道。最后一段甚至承认intern有一个有效的使用场景:繁重的文本处理,例如解析器。得出结论说“[XYZ]如果你不知道自己在做什么是危险的”是如此老生常谈,以至于会造成身体伤害。OutOfMemoryException-不,不是上面的代码,在我的大脑中:链接到javaturning文章,它指向这篇文章,它指向javaturning文章,它…:-虽然你可以看到文章被编辑以添加链接;您可能需要指出,您也是链接到的外部引用的作者。@Carlos链接链接回stackoverflow的外部引用会导致。。Stackoverflow:@Seiti循环引用很容易被检测到

这些天:pCalling intern有其自身的性能影响,使用intern来提高性能需要经过测试,以确保它确实显著加快了您的程序,值得付出额外的复杂性。您还可以使用它来减少具有相对重复值的大型表的内存消耗。然而,在这两种情况下,都有其他更好的选择。是的,实习生有自己的表现影响。特别是因为当您插入字符串并保留对它们的引用时,插入成本会线性增加。至少在sun/Oracle1.6.0_30 vm.From上。仅需补充,堆内存异常有时可以从中恢复,特别是在线程模型(如web应用程序)中。当permgen耗尽时,应用程序通常会永久性地不起作用,并且经常会对资源进行重击直到终止。如果需要,为什么不从子字符串创建一个新字符串呢?这是不对的。当对每个字符串表达式求值时,字符串的插入总是自动发生的。使用的每个唯一字符串都有一个副本&如果出现多个用法,则在内部共享。调用String.intern并不能实现这一切——它只返回内部规范表示。请参阅javadoc。需要澄清-对于编译时常量字符串、文本和固定表达式,实习总是自动进行的。此外,当对运行时动态计算的字符串调用String.intern时,会发生这种情况。因此,您的意思是,如果堆中有1000个Hello对象,而我对其中一个对象执行intern,则其余999个对象将自动销毁?@ArunRaaj否,您的1000个对象仍在堆中,而intern池中还有一个,当str为Hello时,它可以被稍后的str.intern重新使用。这很有趣,但这可能取决于实现。上述潜在内存泄漏在java 1.8和1.7.06及更新版本中没有发生。请参阅。这确认了只有在性能和/或内存分析之后,才在必要时应用微优化。谢谢。请不要只是在你的博客上发布链接,这被一些人认为是垃圾邮件。另外,博客链接有一个显著的趋势,即404死亡。请在此处内联总结您的文章,或在问题的评论中留下该链接。感谢您在@mik1中撰写此文章!非常翔实,清晰和最新的文章。我回到这里是想自己发布一个链接。谢谢你提到-XX arg。您还可以使用它来查看表stats:-XX:+PrintStringTableStatisticsNo,在特定时间内存中的所有内部相等字符串仍然是同一个对象。它将是一个与垃圾收集之前内存中的相等字符串不同的对象。但这没有问题,因为旧字符串已不存在。SoftRef将更有意义。@使用WeakReference而不是SoftReference的vach会更早地释放内存,以便其他分配更快。这取决于应用程序正在做什么,任何一个都可能有意义。我不得不附议:在我的软件上,堆转储显示大多数堆空间被字符串实例使用。当我看到他们的内容时,我看到了很多重复的内容,于是决定改用intern,这节省了数百MB的内存。
#2 = String             #32   // abc
[...]
#32 = Utf8               abc
 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V
private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}