Java 软参考<;字符串>;内存不足时不进行垃圾收集

Java 软参考<;字符串>;内存不足时不进行垃圾收集,java,garbage-collection,soft-references,Java,Garbage Collection,Soft References,我正在测试SoftReference的缓存实现,发现了一个奇怪的行为: 我有一个setName(String name)方法,它通过软引用设置图形对象的名称: public void setName(String newName) { getData().name = new SoftReference<String>(newName,garbagedData); } public void setName(字符串newName){ getData().name=新的软引用

我正在测试SoftReference的缓存实现,发现了一个奇怪的行为:

我有一个
setName(String name)
方法,它通过软引用设置图形对象的名称:

public void setName(String newName) {
    getData().name = new SoftReference<String>(newName,garbagedData);
}
public void setName(字符串newName){
getData().name=新的软引用(newName,garbagedData);
}
(垃圾数据是一个引用队列,在这个特定问题中似乎并不重要)

当我调用
graph.setName(“name”)来自主线程,当强制执行一个
OutOfMemory
错误时,引用所指向的值不会被丢弃,但如果调用
graph.setName(新字符串(“name”)
,则它会被丢弃

我使用Eclipse内存分析器查看堆内容,在这两种情况下,除了软引用链之外,没有其他引用链


如果有人能解释这种奇怪的行为,我很感兴趣。

这很简单。源代码中出现的字符串是加密的,即保存在一个特殊的池中,并且没有机会收集它,因为它被方法的代码引用。当再次调用
setName
时需要它,所以它显然不是垃圾

新字符串(“名称”)
的情况非常不同,因为这会创建原始字符串的副本。实际上,没有必要使用这个构造函数,因为to
String.substring


我猜Eclipse内存分析器不会显示字节码中包含的引用(因为没有人真正需要它)。

您持有对字符串对象的软引用。对于字符串对象,JVM以不同于字符串对象的方式管理字符串文本

String a = new String("test");
String b = new String("test");
在本例中,实例a和b是对不同对象的引用

String a = "test";
String b = "test";
这里a和b都是对同一字符串文字的引用。当您调用graph.setName(“name”)时,它正在创建对字符串文本的引用。垃圾收集通常不考虑引用字符串文本的字符串对象。这将防止字符串对象被垃圾回收


当您调用graph.setName(新字符串(“名称”)时,它正在创建对新字符串对象的软引用。由于引用是新创建的对象,而不是字符串文字,因此可以对其进行垃圾收集。

内存有限的缓存是一个非常糟糕的主意:旧项目将在其使用寿命后很长时间内保留。@kdgregory:我同意这是一个糟糕的主意,但大多数情况下您根本无法控制。但是你通常不知道什么是“使用寿命”,也没有办法平衡不同的缓存(例如:你必须决定“cache-1是否应该限制1000个条目?”而不是“让cache-1和cache-2一起使用不超过100MB”)。在我的例子中,“使用寿命”是最长的。但如果我使用强引用,可能会出现内存不足@kdgregory:如果gc可以在需要时删除对象,那么将对象保存在内存中是件坏事吗?@maaartinus:我总是可以手动将引用设置为null并限制缓存大小,我错了吗?谢谢,我不知道intern字符串和这个特殊池。我怀疑这样的优化。关于
String.substring
的更改,这意味着我只需要调用
substring(0)
而不是构造函数?对我来说,它似乎不是很明确。@zelus:不。在此更改之前,每个字符串都有一个指向可能共享的
char[]
的指针和一个
偏移量和
长度的指针。因此
String s=aVeryLongString.substring(0,1)
是一个非常快速的操作,但是您得到了一个指向巨大的
char[]
的对象。为了解决这个问题,您可以使用
新字符串
,它复制了数组所需的部分。更改后,
substring
执行复制,因此构造函数不再使用。您不应该用
子字符串(0)
或任何其他内容替换它。没必要。