Java WeakReference未收集在花括号中? 这失败了
看起来花括号并不影响弱点Java WeakReference未收集在花括号中? 这失败了,java,garbage-collection,weak-references,linkage,Java,Garbage Collection,Weak References,Linkage,看起来花括号并不影响弱点 一些官方资源?范围是编译时的事情。它不是在运行时确定对象的可达性,只是由于实现细节而产生间接影响 考虑测试的以下变化: static boolean WARMUP; public void testWeak1() throws Exception { variant1(); WARMUP = true; for(int i=0; i<10000; i++) variant1(); WARMUP = false; varian
一些官方资源?范围是编译时的事情。它不是在运行时确定对象的可达性,只是由于实现细节而产生间接影响 考虑测试的以下变化:
static boolean WARMUP;
public void testWeak1() throws Exception {
variant1();
WARMUP = true;
for(int i=0; i<10000; i++) variant1();
WARMUP = false;
variant1();
}
private void variant1() throws Exception {
AtomicBoolean track = new AtomicBoolean();
{
Trackable a = new Trackable(track);
a.toString();
}
if(!WARMUP) System.out.println("variant1: "
+(waitGC(track)? "collected": "not collected"));
}
public void testWeak2() throws Exception {
variant2();
WARMUP = true;
for(int i=0; i<10000; i++) variant2();
WARMUP = false;
variant2();
}
private void variant2() throws Exception {
AtomicBoolean track = new AtomicBoolean();
{
Trackable a = new Trackable(track);
a.toString();
if(!WARMUP) System.out.println("variant2: "
+(waitGC(track)? "collected": "not collected"));
}
}
static class Trackable {
final AtomicBoolean backRef;
public Trackable(AtomicBoolean backRef) {
this.backRef = backRef;
}
@Override
protected void finalize() throws Throwable {
backRef.set(true);
}
}
private boolean waitGC(AtomicBoolean b) throws InterruptedException {
for(int count = 0; count < 10 && !b.get(); count++) {
Runtime.getRuntime().gc();
Thread.sleep(1);
}
return b.get();
}
如果你不能复制它,你可能不得不增加预热迭代的次数
它演示的内容:无论a
是否在范围内(变量2)还是不在范围内(变量1),都无关紧要,在这两种情况下,对象都不是在冷执行时收集的,而是在多次预热迭代后收集的,换句话说,是在优化器启动后收集的
从形式上讲,
a
在我们调用waitGC()
时总是有资格进行垃圾收集,因为从这一点开始它就没有使用过。这是:
可访问对象是可以从任何活动线程在任何可能的连续计算中访问的任何对象
在本例中,由于不存在访问对象的后续计算,因此潜在的连续计算无法访问对象。但是,无法保证特定JVM的垃圾收集器每次都能够识别所有这些对象。事实上,即使是一个根本没有垃圾收集器的JVM也会遵守规范,尽管这可能不是它的意图
代码优化对可达性分析产生影响的可能性也已明确:
可以设计程序的优化转换,以减少可访问对象的数量,使其少于天真地认为可访问的对象的数量。例如,Java编译器或代码生成器可能会选择设置一个不再用于null
的变量或参数,以使此类对象的存储可能更快地被回收
那么技术上会发生什么呢 如前所述,范围是编译时的事情。在字节码级别,保留大括号定义的范围没有效果。变量
a
超出范围,但它在堆栈帧中的存储仍然存在,保留引用直到被另一个变量覆盖或方法完成。编译器可以自由地为另一个变量重用存储,但在本例中,不存在这样的变量。因此,上面示例的两个变体实际上生成相同的字节码
在未优化的执行中,堆栈帧中仍然存在的引用被视为阻止对象集合的引用。在优化执行中,引用只保留到最后一次实际使用。它的字段内联可以允许更早地收集它,直到在构造之后立即收集它为止(或者根本不被构造,如果它没有finalize()
方法)。最极端的结局是
当您插入另一个变量时,情况会发生变化,例如
private void variant1() throws Exception {
AtomicBoolean track = new AtomicBoolean();
{
Trackable a = new Trackable(track);
a.toString();
}
String message = "variant1: ";
if(!WARMUP) System.out.println(message
+(waitGC(track)? "collected": "not collected"));
}
然后,a
的存储在a
的作用域结束(当然,这是特定于编译器的)后被message
重用,并且对象被收集,即使在未优化的执行中也是如此
请注意,关键的方面是存储的实际覆盖。如果你使用
private void variant1() throws Exception {
AtomicBoolean track = new AtomicBoolean();
{
Trackable a = new Trackable(track);
a.toString();
}
if(!WARMUP)
{
String message = "variant1: "
+(waitGC(track)? "collected": "not collected");
System.out.println(message);
}
}
消息
变量使用与a
相同的存储,但其赋值仅在调用waitGC(track)
后发生,因此您可以获得与原始变量相同的未优化执行行为
顺便说一下,不要对局部循环变量使用
short
。Java总是使用int
进行byte
、short
、char
和int
计算(如您所知,例如在尝试写入shortVariable=shortVariable+1;
时),并要求它将结果值剪切为short
(使用shortVariable++
时仍然会隐式发生),添加了一个额外的操作,因此,如果您认为使用short
提高了效率,请注意,事实恰恰相反。这实际上是一个非常好的问题,您也有兴趣知道答案。似乎匿名块会影响可见性,但不会影响可达性,至少可以说这是违反直觉的。什么Sequence
是否要做一些测试?另外,。因为您只是建议GC运行:@AxelH Sequence是一种具有WeakReference列表的类型。@Peterder您可以通过删除序列
来大大改进问题。我敢打赌,您可以直接将其重写为使用WeakReference
,这样就没有人需要猜测了。至少,在问题的顶部解释Sequence
(但重写代码可能更简单)。+++您甚至可以在单个代码段中使用两个WR,一个WR在嵌套块中,另一个WR在提取方法中。
private void variant1() throws Exception {
AtomicBoolean track = new AtomicBoolean();
{
Trackable a = new Trackable(track);
a.toString();
}
String message = "variant1: ";
if(!WARMUP) System.out.println(message
+(waitGC(track)? "collected": "not collected"));
}
private void variant1() throws Exception {
AtomicBoolean track = new AtomicBoolean();
{
Trackable a = new Trackable(track);
a.toString();
}
if(!WARMUP)
{
String message = "variant1: "
+(waitGC(track)? "collected": "not collected");
System.out.println(message);
}
}