我相信这个垃圾是在应用程序的各个地方调用new String时创建的。如何在不每次创建新对象的情况下"创建"字符串?
之所以成为这种垃圾敏感的原因是因为我的应用程序不能创建垃圾,因为我们需要用默认的Java GC实时运行。
1 2 3 4 5 6 7 8 9 10 11
| // you can see I use the same chars array
public String getB37String () {
long l = getLong ();
int i = 0;
while (l != 0L ) {
long l1 = l ;
l /= 37L ;
chars [11 - i ++] = validChars [(int) (l1 - l * 37L )];
}
return new String(chars, 12 - i, i );
} |
例如,使用StringBuilder.toString(),下面使用new String。
1 2 3 4 5 6 7 8
| // and you can see that I use the same builder
public String getString () {
builder. delete(0, builder. length());
char ascii ;
while (0 != (ascii = (char) getUByte ()) && backing. hasRemaining())
builder. append(ascii );
return builder. toString();
} |
号
- 如果你需要使用相同的字符串内容,例如"Java",如果你使用String ="Java",第一次它将创建一个新的对象,之后,没有新的对象被创建,它们是从字符串池中提取的。如果每次创建新对象时都使用new,不管它是否已存在于池中,因此无论哪种方式,每次分配新内存时都将始终创建新的字符串内容
- 我仍然不清楚你问题背后的动力是什么。您是否遇到性能问题?您的标题表明您可能有太多的char数组。如何避免创建新的字符串来帮助您?这似乎是一个臭名昭著的谬论问题。你能澄清一下吗?
- @sstan这些char数组是支持许多字符串的数组。我的应用程序中需要零垃圾,使用new String和StringBuilder.toString都会产生垃圾。我想知道是否有任何技巧可以在文本池中创建字符串(或者其他一些防止垃圾的解决方案)!
- @吉尔:我的观点是,即使你能做到这一点,你目前正在传递给String构造函数的char数组,或者支持StringBuilder的char数组又如何呢?那些垃圾也不是吗?但不管你在做什么,你不需要这些吗?您真的确定避免创建这个String实例会真正解决您的问题吗?
- @sstan如果您注意到这些方法没有生成新的char数组或StringBuilder,而是重用这些对象。姜饼头下面似乎有解决办法!
- @吉尔:因为我不认为intern()的想法会对你想要实现的目标起作用,所以我认为关键是理解你的方法getB37String()和getString()是如何被调用和使用的。是否确实需要返回字符串?你能改为播放流媒体吗?否则,我看不到如何避免创建在某个时刻需要收集的对象。您是否会从寻找调优GC如何执行集合的方法中获益更多?(多久、多久等…)
- @Jire,"在文本池中创建字符串"不会神奇地阻止垃圾。它仍然需要被创建。当它不再使用时,它仍然会被垃圾收集。字串文字不神奇。他们只是实习池的一部分。除非你打算预先分配你需要的所有可能的字符串。
第一:观测
The reason for being this garbage-sensitive is because my application cannot create garbage as we need to run close to real-time with the default Java GC.
如果是这样("无法创建垃圾"实际上是statement1 a true),那么你有可能会开始在错误的地方在您的Java作为实现语言的采摘。
Java设计的假设是在一代的垃圾是好的。它是"成本"的固有的复杂性(和随之而来的网络错误显示内存管理)做这个假设pervades设计语言和标准库的设计。
另一件事是"Java是不支持它的是"良好的OO设计原则的强烈支持。特别是,有一些例外,漫游的API是提供强大的抽象设计的陷阱,可以在应用程序在处理您的休息。
例如,当你这样做:
1 2 3
| char[] c = new char[]{'a', 'b', 'c'};
...
String s = new String(c ); |
一个新的String构造函数和拷贝allocates char[]c人物在靠近它。为什么?因为如果它不,你会有一个"漏抽象"。有人可以这样做:
1 2 3 4 5
| char[] c = new char[]{'a', 'b', 'c'};
...
String s = new String(c );
...
c[0] = 'd'; |
安切洛蒂和漏水的抽象变化进入一个不可变的对象(supposedly)。
那么什么是"解决方案"?
你可以重写你的应用程序在C或C + +编程语言或其他一些有过在那里你可以完全控制内存分配。(当然,这是一个焊料的工作……有可能是其他原因,你为什么不能这样做。)
你可以重新设计你的应用程序有关的部分,这样,他们不使用String或StringBuilder或任何标准的Java类,涉及显式或隐式(堆配置在罩)。这是不可能的,但它是很多的工作。例如,许多标准和第三方API,你的护照,他们String对象作为参数。
你可以在你的代码分析部分做字符串操作是这样做"聪明的"分配,为了减少垃圾。
不幸的是,所有这些事情都可能让你更大的代码库,读写硬,硬到调试和功能。
案例1:一个它的真正的问题是,如果你真的试图解决的GC是休息。有地址的GC的方式来休息,不要去只要不产生任何垃圾。例如,撬一低GC暂停平行,和减少的大小的空间可以给年轻的一代,是你休息的noticeable短足以。另一个技巧是在A点的力气,当你的用户不知道,例如,当加载一个新的通知书;游戏中一级。
如果您使用的是Java8u20或更高版本,您可以尝试使用-XX:+UseG1GC -XX:+UseStringDeduplication启用字符串重复数据消除。
虽然这不能避免创建垃圾,但它可能会降低内存压力。
如果您真的想在不复制char[]数组成本的情况下创建String实例,则必须通过反射访问包私有构造函数java.lang.String.String(char[], boolean)或私有char[] value字段,并进行适当的运行时检查/错误报告,以确定它是否实际工作。
我不推荐,但这是一种选择。
另一种选择是停止使用字符串并使用ByteBuffer。您可以根据需要对它们进行切片、返回视图、返回只读视图、回收它们。
如果使用UTF-8数据,它们也会更紧凑。缺点是您不能使用需要字符串的API。
或者在尽可能多的地方处理CharSequence/StringBuilder/CharBuffer对象。
根据用例的不同,还可以为计算创建一个字符串缓存。Map,其中t是计算的输入参数。这样,对于EDOCX1的每个可能值(1),您只需要一个字符串。
1
| return new String(chars, 12 - i, i ); |
号
注意,对于Java 8,字符串不存储内部偏移,即字符串对象在某些较大的后备字符数组上不是"视图"。
这在过去是不同的,但由于它是一个实现细节,所以它被改变了。
通过bootstrap类加载器添加一个自定义字符串类可能会撤销这一更改,但这更有可能破坏或导致严重的性能下降。
as we need to run close to real-time with the default Java GC.
号
这可能是你的实际问题。
默认情况下,配置的任何收集器都不会提供任何接近实时行为的内容。与串行或并行收集程序相比,CMS或G1可以提供更低的暂停时间,尤其是在大型堆上。
- 我很肯定字符串重复数据消除不会有帮助。操作的问题是产生的垃圾量。重复数据消除是为了减少表示非垃圾字符串所需的空间。
- @他声称产生的垃圾量是问题所在。我不确定事实是否如此。生成更多的垃圾会导致GC更频繁地运行,只要对象寿命足够短,它不一定会增加暂停时间。不管怎样,我提到了各种其他的选择。
- 是的。你确实建议其他的选择。但我建议你提出的第一个选择不太可能有帮助。写这样的答案不是个好主意…
- @斯蒂芬。这些问题被称为"Java高量的字符[],如何减少?".I的第一个建议是这样做,减少堆中char数组的数量。¯;(_)_/&175;。老实说,我很难提取出他真正想要的东西。所以我只是把选择扔出去。有些东西可能会卡住。
- 我知道。但是你没有"理解"我对你说的话。
- @8472很抱歉之前没有评论,但我想说我很喜欢你的回答。我最终使用了您的建议来直接访问构造函数。这种反射动作需要对CPU的更多需求,所以我最终通过哈希创建了自己的池,因此我自己的无垃圾散列映射(因为Java产生了垃圾)。此后,我不再使用反射方法,而是简单地迭代每个字符,并使用自动插入的连接。我有同样的池。:)
- @Jire,您可以将反射方法转换为方法句柄,并通过lambdametafactory调用它们,这样可以避免反射的成本。
两者之间的差异
参考资料在这里。
They both are the same, they are like any other object but:
Since String is one of the most used type in any application, Java
designer took a step further to optimize uses of this class. That's
why they come up with an idea to cache all String instances created
inside double quotes e.g. "Java". These double quoted literal is
known as String literal and the cache which stored these String
instances are known as as String pool.
At high level both are String object, but main difference comes
from the point that new() operator always creates a new String
object. Also when you create String using literal they are interned.
Here two different objects are created and they have different
references:
Similarly when you compare a String literal with an String object
created using new() operator using == operator, it will return false,
as shown below :
号垃圾收集器
参考资料在这里。
In fact the String objects that correspond to String literals
typically are not candidates for garbage collection. This is because
there is an implicit reference to the string object in the code of
every method that uses the literal. This means that the String is
reachable for as long as the method could be executed.
However, this is not always the case. If the literal was defined in a
class that was dynamically loaded (e.g. using Class.forName(...)),
then it is possible to arrange that the class is unloaded. If that
happens, then the String object for the literal will be unreachable,
and will be reclaimed when the heap containing the interned String
gets GC'ed.
号字符串池
参考文献在这里。
java.lang.String.intern() returns an interned String, that is, one
that has an entry in the global String pool. If the String is not
already in the global String pool, then it will be added.
号
通过编程,您可以遵循以下方法:
因此,对于任意两个字符串s和t来说,s.intern() == t.intern()是真的,前提是s.equals(t)是真的。
因此,如果在字符串上使用intern():
打电话给String.intern()。
然后:
保证来自唯一字符串池。
- 我知道字符串是缓存的,我想知道是否可以通过new String或其他方式实现相同的行为。
- 正如您可以阅读的那样,当使用新的Java运行时不再在池中搜索时,它将直接在内存中分配一个新的位置,因此创建它的速度更快。
- 我知道这一点。我想在游泳池里搜索!有没有办法接近它?
- intern看起来像我需要的!顺便问一下,StringConstantPool的用途是什么?intern不会使用String内的池吗?
- 您是正确的,这只是一个例子,演示了如何使用Java作为常数池变量来表示字符串池。看看我的编辑。
- 那么,使用intern()如何避免创建String实例?您不需要先创建字符串,然后才能调用intern()并从池中获取等效的字符串吗?
- @这正是我想的。我不确定如果我必须首先使用new String,它将如何工作,就像在我的第一个示例new String(chars, 12 - i, i).intern();中一样。
- 好吧,如果你申请实习生,你可以确保你手中的价值来自游泳池。因此,在池中应该只有一个具有此值的字符串,所以如果下次尝试声明具有相同值的字符串,那么您将处于安全的一面,因为您可以确保没有创建新的字符串。
- @我不明白,你能举个例子吗?
- 看看这个例子:JavaCoDeGeKE.COM/核心Java/Lang/String/& Helip;
- @所以,即使我使用new String,如果我使用intern,它也不会创建另一个实例?
- 不,它将创建,从不使用新的
- @姜饼头怎么样?实际上,我希望在不使用new的情况下使用池,但我知道没有new的情况下无法创建字符串。
- -1,因为这个答案抄袭了链接博客帖子和其他堆栈溢出答案中的内容。垃圾收集部分从这里复制,字符串池部分从这里和intern()文档中被剽窃。
- @Vulcan的网址在回答中声明得像在阅读更多,请随意在回答中添加所有引用,我没有时间全部添加,我只添加了一个。
- @不好意思,它仍然是剽窃行为,除非你清楚地表明你在引用另一个源代码;这就是块引号标记存在的原因。此外,在其他两个部分中的任何一个都没有留下与原始内容的链接;这显然是抄袭,毫无疑问,也没有这样的地方。您应该编辑您的答案,以彻底指出您从其他地方引用的内容(通过blockquote标记),并正确链接到这些源。
- 仅仅从其他来源复制粘贴而不明确说明您不是文本的原始作者,这对原始作者是不公平的。
- @我完全知道这一点,正如我之前说的,我没有时间,我只引用了一个,既然你知道所有的参考文献,请随意添加它们。
- @如果我确信我找到了你剽窃的每一个来源,我会自己编辑引文和黑引号,但因为我不确定你从哪里准确地复制了什么(我发现的三个来源没有完全覆盖你的答案),我无法自信地修复你的答案。
- @你真的应该使用blockquote,因为它们确实是从其他来源引用的。块引用是在堆栈溢出时逐字引用另一个源代码的正确方法。
- 让我们在聊天中继续讨论。
- @我们能继续聊吗
- @我不相信在这里聊天是必要的,反正我就要离开我的办公桌了。如果您对什么被认为是剽窃,或者如何正确添加属性有任何疑问,您可以参考这个伟大的资源。感谢您帮助解决您的问题:)