源码一般存在哪里_按一次F5,跟一次源码

2020/05/23

听首歌吧

最近复盘自己的项目

总会遇到很多代码底层实现问题

比如一个数组的扩容实现过程

比如一个文件读取经过多少次IO操作还是NIO操作

所以这次搭建一个JAVA源码学习的环境

系统环境:

版本:jdk1.8.0_121

IDE:IDEA2019

步骤:

首先创建一个纯JAVA环境的项目

然后在项目根目录下创建 source和test两个文件夹

ba3b2b55a7417b2eb535ff050c2b98b8.png

source用来存放JAVA源码的代码  
test可以根据个人需要创建不同的程序 测试 跟进 底层代码

那么我们就有个问题了→Java源码在哪里可以Get到

其实在java_home里就有  看自己当时的java源码存放位置 

这里我存放的是个人C盘下的 jdk1.8.0_121文件夹中 

如图下所示src解压之后就是你需要的java源码了

f83f6d94b6fb6eeb44d387380af1256a.png

将其解压后copy到刚刚创建的source目录下

edd2aa00562e989892b256be8a0f3e67.png

复制好后就可以在test文件下创建一个启动测试类  这里我

用hashmap举个例子

ff7e4dd35ecc7dca320f4bcaccdf699c.png

启动后就可以跟源码了  但是一般到这里F5或者step into是进不去的

会遇到以下的问题:

  • 系统资源不足,抛出 OutOfMemory 异常;

  • 程序包 UNIXToolkit FontConfigManager 不存在;

  • 找不到 com.sun.tools.xx 等 类。

需要作出一些调整 

1.  在Setting → Build, Excution, Deployment → Compiler  

     修改Build process heap size 700 到1700或以上

7f092998ca3ac94232d69e3298d8f2b8.png

2.  程序包不在 可以在你原来的jdk环境中  找 lib 文件夹下的 tools.jar  包  复制到项目中  

在 project structure中  lib引用中 添加自己的tools.jar 

d151535ea21f310f60e410e5917ff6df.png

3.  在Setting → Build, Excution, Deployment → Debugger → Stepping

取消 Do not step into the class 选项

40f6dca563ebaad4075a805e9bbaa495.png

到这里应该就可以单步进入一个hashmap的源码实现了

康康一个hashmap创建 

到存放一个K V键值对的实现是怎样的

4ae8234794f4af1b5ac63118b4e5db82.png

首先会创建一个hashmap对象

e5205bffbb10ec9722fe8b1f48918071.png

接着会跟到一个抽象map 完成一个object的创建  到这一个hashmap就算创建完成了

再看看放入一个的实现过程

3319779782c941b4a8884ca9c349e7d3.png

1. 对于键值对 Key value形式的存放 一般都是创建value值对象

4316a606be03a1a4de30f79bafaa6d4a.png

再到key值创建

36503700a3460eb4e54083b06a00712d.png

2. 当Key 和 value都创建完成后 开始hashmap的存放 

626cc151ec65b5728638c96fe7bb0ca5.png

首先会进行一个hash函数的计算

这里用到的是三目表达式,

如果key没有就返回0 否则对key值的hashcode

计算并右移16位

我看过很多解释为什么要对计算的一个值进行右移16位

因为在hashcode创建时 

public int hashCode() {//哈希函数int h = hash;
if (h == 0 && value.length > 0) {char val[] = value;//把每一个字符取出来 31 做一次 次幂求和计算for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;}

会对Key值的每一个字符 即每一个char进行*31的操作

之所以使用 31, 是因为他是一个奇素数。如果乘数是偶数,

并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算(低位补0)。

使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果。 

31 有个很好的性能,即用移位和减法来代替乘法,

可以得到更好的性能:31 * i == (i << 5) - i, 

现代的 VM 可以自动完成这种优化。这个公式可以很简单的推导出来。

知道了这个hashcode的初始计算方式  我们看看为什么是右移16位 而不是15 或者 17

在分析这个问题之前,我们需要先看看另一个事情,什么呢?就是 HashMap 如何根据 hash 值找到数组种的对象,我们看看 get 方法的代码:

db571cc9ccc1878760b03198f99c4d3a.png

这里会有一次创建一个node对象的操作 

接着看下面

final Node<K,V> getNode(int hash, Object key) {Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&// 我们需要关注下面这一行(first = tab[(n - 1) & hash]) != null) {if (first.hash == hash && // always check first node((k = first.key) == key || (key != null && key.equals(k))))return first;
if ((e = first.next) != null) {if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}
}return null;}

我们看看代码中注释下方的一行代码:first = tab[(n - 1) & hash])。

使用数组长度减一 与运算 hash 值。

这行代码就是为什么要让前面的 hash 方法移位并异或。

我们分析一下:

首先,假设有一种情况,

对象 A 的 hashCode 为 1000010001110001000001111000000,

对象 B 的 hashCode 为 0111011100111000101000010100000。

如果数组长度是16,也就是 15 与运算这两个数, 

你会发现结果都是0。

很明显不是一个好的散列算法。

但是如果我们将 hashCode 值右移 16 位,

也就是取 int 类型的一半,刚好将该二进制数对半切开。

并且使用位异或运算(如果两个数对应的位置相反,则结果为1,反之为0),

这样的话,就能避免我们上面的情况的发生。

总的来说,使用位移 16 位和 异或 就是防止这种极端情况。

但是,该方法在一些极端情况下还是有问题,

比如:

10000000000000000000000000 

和 1000000000100000000000000 

这两个数,如果数组长度是16,那么即使右移16位,

在异或,hash 值还是会重复。

但是为了性能,对这种极端情况,

JDK 的作者选择了性能。

毕竟这是少数情况,为了这种情况去增加 hash 时间,性价比不高。

最后:

我们完成了一个hashmap的Key value值存放  在最后的代码中 

计数器会加一

如果计数器大于原来hashamap的大小会进行一次扩容 

扩容大小是原来的50%

题外话 如果是队列或者concurrentHashmap中 扩容大小就是100% 

(因为要保证线程安全 他们都在源码中加入了synchronize关键字)

8243e4855e6ba45eee32ef243b997925.png

至此我们跟一个对象的创建到使用的源码过程已经完成

我也查看了一下对于一个初学者或者源码学习的范围  大概是以下图中我画的范围

先看Java目录下的IO lang math net nio time util 7个包下的代码即可

902786d8fc24fc55f431f6369986c690.png

之前从来没有跟过源码  看一个对象的创建和调用过程中如何学习 

也是看了B站Codesheep一次源码学习经验分享

才动手创建一个自己的源码学习环境

我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线我是分割线

听首抒情的曲子

上个星期见了很久没见的朋友  

和三五好友一起吃饭 

见了自己班上的老大班长  

仿佛像开学了一样

希望各自前程似锦,生活安康

34ac400320888c55525a298840227403.png

第一次看到日落下的猎德大桥与广州塔一起并入镜头的美景

1f6c985b052397e766baf2bfe1e6a44c.png

本以为是随意的一餐,结果特别正式的场景差点以为要西装领带出场了

79adfa56559f3ae35dad3fa6a144d870.png

最后也是四人第一次吃饭,彼此都很熟悉

可能就需要一个这样的机会和场景

线上有多热闹

我就有多想见你

因为是你们,所以,多远都会过来吃一餐

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

有缘广州再约有缘广州再约有缘广州再约有缘广州再约

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值