源码随想 String -> SoftReference

2021年7月27日 15:38:14

今天实习时看 String的源码,发现其中的一个构造方法

public String(byte bytes[], int offset, int length, String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null)
throw new NullPointerException("charsetName");
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(charsetName, bytes, offset, length);
}

StringCoding.decode()方法处点入之后,发现其实现如下:

static char[] decode(String charsetName, byte[] ba, int off, int len)
throws UnsupportedEncodingException
{
StringDecoder sd = deref(decoder);
String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
|| csn.equals(sd.charsetName()))) {
sd = null;
try {
Charset cs = lookupCharset(csn);
if (cs != null)
sd = new StringDecoder(cs, csn);
} catch (IllegalCharsetNameException x) {}
if (sd == null)
throw new UnsupportedEncodingException(csn);
set(decoder, sd);
}
return sd.decode(ba, off, len);
}

deref()方法处点入,发现其实现如下:

private static <t> T deref(ThreadLocal<softreference<t>> tl) {
SoftReference<t> sr = tl.get();
if (sr == null)
return null;
return sr.get();
}

没想到,这个 decoder居然是一个存放在 ThreadLocal里的 软引用!!!

关于 tl.get(),也就是 ThreadLocal :: get(),虽然还没看过这部分代码,但是在马士兵的课上和一些面经上面都强调过它的重要性,在这里一看到,忽然有一种他乡遇故知的感受。

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

不止如此,在 sr.get()方法中,有两个很奇怪的量 clocktimestamp

public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}

进入到 SoftReference.java类中之后,有如下定义:

    /**
* Timestamp clock, updated by the garbage collector
*/
static private long clock; /**
* Timestamp updated by each invocation of the get method. The VM may use
* this field when selecting soft references to be cleared, but it is not
* required to do so.
*/
private long timestamp;

也就是说,clock会在每次进行垃圾收集的时候更新;timestamp会在每次方法调用的时候更新。JVM通过对 timestamp字段的监测来判断是否需要清楚弱引用。

那么到底什么时候真正进行软引用的清理呢?

首先,软引用中有一个全局变量 clock,在每一次垃圾回收的时候,它的值都会更新(in millis)。

同时,每一个软引用都有一个 timestamp变量,当前软引用每次被访问的时候都会更新(包括 初始化 和 调用get()方法)

当一次新的垃圾回收开始时,是否要回收一个软饮用对象依据如下:

  1. timestamp的年龄多大了
  2. 剩下的堆空间还有多少

具体的计算方式如下:

  • free_heap为堆中的空余空间大小(以 MB为单位)
  • interval是上一次 GC和 现在timestamp的时间差,
  • ms_per_mb是一个常量,保持堆中空余的每MB空间中的 软引用 的毫秒值
interval <= free_heap * ms_per_mb

举个例子:

如果当前的某个软引用 A,它的 timestamp值为 2000ms,上一次 GC的 clock时间是 5000ms,ms_per_mb常量的大小是 1000,空闲的堆空间为 1MB,那么,计算可得

5000 - 2000 <= 1 * 1000 是假的,返回 false

所以我们需要清理掉这个软引用。

那么如果假设堆空间比较大,比如是 4MB,那么计算可得

5000 - 2000 <= 4 * 1000 是真的

所以我们不需要清理这个软引用


需要注意到的是:

一个软引用在一次访问之后,至少会存活一次 GC。

因为:

interval的值是我们通过 clocktimestamp计算出来的,而 clock是上一次的 FGC时间,既然有上一次的 GC,说明它肯定至少得活过一次 GC呀。

此外,每当一个引用在某次垃圾回收之后被访问到了,那么 timestamp的值会被设置成和 clock一样,然后 interval就变成了 0,软引用就肯定不会被回收了。

想想也是的呀,针对经常在用的对象,我们肯定是要保留的呀。</softreference

最新文章

  1. html视频播放器的代码 及其参数详解
  2. 阻止网页内部滚动条mousewheel事件冒泡
  3. scala Ordering
  4. 一个超复杂的间接递归——C语言初学者代码中的常见错误与瑕疵(6)
  5. FFT(1)
  6. 如何用Java解析CSV文件
  7. POJ 2653 Pick-up sticks(判断线段相交)
  8. 修改Zabbix默认运行账户
  9. 探秘Java虚拟机——内存管理与垃圾回收
  10. WPF拖动绘制
  11. css三角形绘制
  12. html绑定
  13. android 报错之noclassdeffounderror
  14. 镜像命名的最佳实践 - 每天5分钟玩转 Docker 容器技术(18)
  15. Quartz + Tablesaw 报表统计
  16. 2019.4.10 初识puppeteer
  17. 规则引擎 - (三)BOM工程(上)
  18. seriviceWorker 小结
  19. oracle存储过程和存储函数&amp;触发器
  20. iPhone X Web 设计

热门文章

  1. 第十二天python3 匿名函数
  2. ES6中class方法及super关键字
  3. JPA作持久层操作
  4. Vector3类定义
  5. CF 559C - Gerald and Giant Chess (组合计数)
  6. 重看Java教学视频时的查漏补缺
  7. 如何自定义一个Collector
  8. 技术管理进阶——技术Leader需要数据思维
  9. 【java】学习路线5-public和private、构造方法、this关键字、封装对象、static关键字、main方法结构解析
  10. Javaweb___Ajax和Json