源码随想 String -> SoftReference
源码随想 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()方法中,有两个很奇怪的量 clock和 timestamp
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()方法)
当一次新的垃圾回收开始时,是否要回收一个软饮用对象依据如下:
timestamp的年龄多大了- 剩下的堆空间还有多少
具体的计算方式如下:
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的值是我们通过 clock和 timestamp计算出来的,而 clock是上一次的 FGC时间,既然有上一次的 GC,说明它肯定至少得活过一次 GC呀。
此外,每当一个引用在某次垃圾回收之后被访问到了,那么 timestamp的值会被设置成和 clock一样,然后 interval就变成了 0,软引用就肯定不会被回收了。
想想也是的呀,针对经常在用的对象,我们肯定是要保留的呀。</softreference
源码随想 String -> SoftReference的更多相关文章
- JDK源码学习--String篇(二) 关于String采用final修饰的思考
JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...
- 深入源码剖析String,StringBuilder,StringBuffer
[String,StringBuffer,StringBulider] 深入源码剖析String,StringBuilder,StringBuffer [作者:高瑞林] [博客地址]http://ww ...
- JDK源码之String类解析
一 概述 String由final修饰,是不可变类,即String对象也是不可变对象.这意味着当修改一个String对象的内容时,JVM不会改变原来的对象,而是生成一个新的String对象 主要考虑以 ...
- JDK1.8源码学习-String
JDK1.8源码学习-String 目录 一.String简介 String类是Java中最常用的类之一,所有字符串的字面量都是String类的实例,字符串是常量,在定义之后不能被改变. 二.定义 p ...
- JDK源码学习--String篇(四) 终结篇
StringBuilder和StringBuffer 前面讲到String是不可变的,如果需要可变的字符串将如何使用和操作呢?JAVA提供了连个操作可变字符串的类,StringBuilder和Stri ...
- JDK源码学习--String篇(三) 存储篇
在进一步解读String类时,先了解下内存分配和数据存储的. 数据存储 1.寄存器:最快的存储区,位于处理器的内部.由于寄存器的数量有限,所以寄存器是按需分配. 2.堆栈:位于RAM中,但是通过堆栈指 ...
- JDK源码学习--String篇(-)
工作三年了,用了三年的JAVA,突然发现竟然没有好好的看下JDK的源码,整天用着的String,只是大概知道怎么回事,其中的实现逻辑却是一头雾水. 知耻而后勇,加油!!! java.lang.Stri ...
- java基础进阶一:String源码和String常量池
作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8046564.html 邮箱:moyi@moyib ...
- [Java源码解析] -- String类的compareTo(String otherString)方法的源码解析
String类下的compareTo(String otherString)方法的源码解析 一. 前言 近日研究了一下String类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, ...
随机推荐
- Postgres常用时间查询
如select extract(day from now());
- React报错之map() is not a function
正文从这开始~ 总览 当我们对一个不是数组的值调用map()方法时,就会产生"TypeError: map is not a function"错误.为了解决该错误,请将你调用ma ...
- ETCD快速入门-01 ETCD概述
1.ETCD概述 1.1 ETCD概述 etcd是一个高可用的分布式的键值对存储系统,常用做配置共享和服务发现.由CoreOS公司发起的一个开源项目,受到ZooKeeper与doozer启发而 ...
- Java学习第七周
这周学习了集合,stream流等知识 List集合 – List系列集合:添加的元素是有序,可重复,有索引 1.ArrayList: 添加的元素是有序,可重复,有索引 2.LinkedList: 添加 ...
- 牛客网 十二桥问题(状压DP)
https://ac.nowcoder.com/acm/contest/1104/B 注意到\(\text{K}\)只有\(12\),因此对起点与每个毕经边对应的点单源最短路,\(\text{DP}\ ...
- 【游记】CSP 2021 J2
这次是第一次参加CSP的复赛,所以考的就很LJ. \(DAY-\infty\) 到 \(DAY-14\) 知道了自己苟过了初赛,像个SB一样. (我初赛66分,旁边那位63.5,cao着线过去的) \ ...
- Codeforces 1715E - Long Way Home
又是废掉的一个div2啊 第一次在学校熬夜打cf,开心还看到了自己最喜欢的斜率优化ohhh 链接 :E - Long Way Home 看到那个平方就可以靠感觉认为是斜率优化了.... 感觉似不似有点 ...
- KubeEdge:下一代云原生边缘设备管理标准DMI的设计与实现
摘要:KubeEdge设备管理架构的设计实现,有效帮助用户处理设备数字孪生进程中遇到的场景. 本文分享自华为云社区<KubeEdge:下一代云原生边缘设备管理标准DMI的设计与实现>. 随 ...
- HOSMEL:一种面向中文的可热插拔模块化实体链接工具包
HOSMEL: A Hot-Swappable Modularized Entity Linking Toolkit for Chinese ACL 2022 论文地址:https://aclanth ...
- OpenJudge1.5.17 菲波那契数列
17:菲波那契数列 总时间限制: 1000ms 内存限制: 65536kB 描述 菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和. 给出一个正整数k,要求 ...