源码随想 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类的一些方法, 通过查看源码, 对一些常用的方法也有了更透彻的认识, ...
随机推荐
- 关于奇妙的 Fibonacci 的一些说明
奇妙的 Fibonacci,多次模拟赛中出现 同时也是 BZOJ 2813 一 Fibonacci 的 GCD 如果 \(F\) 是 Fibonacci 数列,那么众所周知的有 \(\gcd(F_i, ...
- [eJOI2019]异或橙子 题解
简要题面 维护一个数据结构,支持单点修改,询问区间所有子区间的异或和的异或和 . 做法 首先,题目要求所有子区间的异或和的异或和,发现每个元素异或两次就变成 \(0\),所以考虑统计每个元素出现的次数 ...
- Thingsboard硬网关金鸽BL102采集三菱PLC步骤
PLC网关金鸽BL102:采集三菱FX-5U数据如何转成MQTT上报?金鸽BL102PLC网关时一款功能强大的PLC数据采集网关,南向可以采集主流的PLC,如三菱.西门子.台达.欧姆龙.施耐德等等PL ...
- 小试牛刀:Go 反射帮我把 Excel 转成 Struct
背景 起因于最近的一项工作:我们会定义一些关键指标来衡量当前系统的健康状态,然后配置对应的报警规则来进行监控报警.但是当前的报警规则会产生大量的误报,需要进行优化.我所负责的是将一些和用户行为指标相关 ...
- SingleSphere类定义
针对于单个球体的碰撞,其实和多个球体一样的,只是我修改了书上的代码,定义了这么多类,终于到头了.马上就要展示奇迹的时候了. 类定义: #pragma once #ifndef __SINGLESPHE ...
- RISC-CPU设计和 FPGA 实现
摸鱼的时候找到一份单周期和多周期的riscv的fpga实现,还是挺符合我的预期的 知乎专栏地址:https://www.zhihu.com/column/c_1530950608688910336
- 常见docker命令(二)-容器生命周期相关
docker run 命令主要参数-d 后台运行,返回容器id-i 以交互模式运行,通常与-t连用-t 为容器重新分配一个伪输入终端,通常与-i连用-P(大写) 随机端口映射,容器内部端口随机映射到主 ...
- Luogu3092 [USACO13NOV]没有找零No Change (状压DP)
将金币状压,然后就没多说的了. #include <iostream> #include <cstdio> #include <cstring> #include ...
- 业界压测平台与JMeter的对比
压测平台是什么? 压测,即压力测试,作用是对各种服务对象进行压力测试以获得该服务处于或超过预期负载时系统的运行情况,进而判断系统在峰值负载或超出最大负载情况下的处理能力. 压测工具,顾名思义,就是用来 ...
- Spring5中JdbcTemplate
JdbcTemplate是什么 JdbcTemplate 类提供了很多便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处理. 在spri ...