java—重写同步锁中使用的变量是否会阻止其垃圾回收?

o8x7eapl  于 2021-07-09  发布在  Java
关注(0)|答案(1)|浏览(358)

我的服务中有一个缓存作为成员变量,我正在创建一个方法,通过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块中旧缓存的引用,那么当执行退出该块时,它是否仍会将旧缓存对象标记为已锁定-使其无法用于垃圾收集?

h9vpoimq

h9vpoimq1#

通过查看为类似情况生成的字节码,我们可以看到锁定字段的对象地址是重复的,用于获取和释放锁。所以原始锁定对象用于锁定。
在同步块中使用新对象更改锁定字段后,另一个线程可以获取新对象的锁,并可以进入同步代码块。像这样使用synchronized不会在线程之间提供同步。您应该使用另一个对象进行锁定(例如final object cachelock=new object())
仅出于信息目的,这种用法并不能阻止垃圾回收。因为这里提到的对象地址在这个方法的堆栈帧内,一旦这个方法完成执行,堆栈帧就会被销毁,对旧对象的引用也就不存在了(但不要像这样使用synchronized。)
您可以在这里检查jvm指令集

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
    .....

相关问题