我在我的服务中有一个缓存作为成员变量,我正在创建一个方法来通过 JMX MBean 公开它,这样我就可以在运行时使用新的缓存到期时间拆除并重新创建我的 vogon 缓存:
public class CachedVogonService implements CachedVogonServiceMBean {
private LoadingCache<String, Vogon> cache;
private long expiryInSeconds;
private VogonService service;
public CachedVogonService(VogonService newService,
long newExpiryInSeconds) {
this.expiryInSeconds = newExpiryInSeconds;
this.service = newService;
this.cache = createCache(newService, newExpiryInSeconds);
}
private LoadingCache<String, Vogon> createCache(
VogonService newService,
long expiryInSeconds) {
return CacheBuilder.newBuilder()
.refreshAfterWrite(expiryInSeconds, TimeUnit.SECONDS)
.build(new VogonCacheLoader(
Executors.newCachedThreadPool(), newService));
}
/**
* This is the method I am exposing in JMX
*/
@Override
public void setExpiryInSeconds(long newExpiryInSeconds) {
this.expiryInSeconds = newExpiryInSeconds;
synchronized (this.cache) {
this.cache = createCache(service, expiryInSeconds);
}
}
我担心我的锁定技术会导致 JVM 保留对旧缓存的引用并防止它被垃圾收集。
如果我的服务对象丢失了对同步块(synchronized block)内旧缓存的引用,那么当执行退出该 block 时,它可能会留下旧缓存对象仍然标记为锁定 - 使其无法用于垃圾回收吗?
最佳答案
通过查看为类似情况生成的字节码,我们可以看到锁定字段的对象地址是重复的,用于获取和释放锁。所以原始锁定对象用于锁定。
当你在synchronized block 中用新对象改变locking字段后,另一个线程可以获得新对象的锁,并可以进入synchronized代码块。像这样使用同步,不提供线程之间的同步。您应该使用另一个对象进行锁定。 (比如 final Object cacheLock = new Object())
仅供引用,这种用法不会阻止垃圾收集。由于这里提到的对象地址都在这个方法的栈帧中,一旦方法执行完毕,栈帧就会被销毁,旧对象的引用也不会留下。 (但不要像这样使用同步。)
可以查看JVM指令集here
public class SyncTest {
private Long value;
public static void main(String[] args) {
new SyncTest().testLock();
}
public SyncTest() {
value = new Long(1);
}
private void testLock() {
synchronized (this.value) {
this.value = new Long(15);
}
}
}
private void testLock();
0 aload_0 [this]
1 getfield t1.SyncTest.value : java.lang.Long [27] // push value field to operand stack
4 dup // duplicate value field push it to operand stack
5 astore_1 // store the top of the operand stack at [1] of local variable array (which is the duplicated value field)
6 monitorenter // aquire lock on top of the operand stack
7 aload_0 [this]
8 new java.lang.Long [22]
11 dup
12 ldc2_w <Long 15> [31]
15 invokespecial java.lang.Long(long) [24]
18 putfield t1.SyncTest.value : java.lang.Long [27]
21 aload_1 // load the [1] of local variable array to the operand stack (which is the duplicated value field previously stored)
22 monitorexit // release the lock on top of the operand stack
23 goto 29
26 aload_1
27 monitorexit
.....
关于java - 覆盖同步锁中使用的变量是否会阻止其垃圾回收?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54518024/