CurrentHashMap巧妙利用位运算获取数组指定下标元素
先来了解一下数组对象在堆中的存储形式【数组长度,数组元素类型信息等】+ 【存放元素对象的空间】
Ma
基础信息 | 实例数据 | 内存填充 | |
Mark Word,ClassPointer,数组长度 | 第一个元素 | 第二个元素 | 固定的填充内容 |
所以我们想要获取某个下标的元素首先要获取这个元素的起始位置和每个元素长度
例如我们要取第二个元素的,首先知道第二个元素的起始位置,从其实位置开始按照每个元素的长度取指定的位数,就相当于获取了第二个元素的全部信息。
再来了解一下Java中一个特殊的类sun.misc.Unsafe
sun.misc.Unsafe是JDK提供的用于很底层编程的类,位于sun.misc包中。在有些底层编程的场景,Java语言层面办不到的事情,我们可能需要使用JNI,借助C语言去实现。但是使用JNI并不是唯一的选择,使用JNI会将代码绑定到特定的平台,使用Unsafe类可以保留Java语言代码对平台的独立性,又实现底层编程。
Unsafe类并没有public的构造函数,只提供了一个静态工厂方法,这个静态方法还只提供给JDK标准库自身的类调用,在我们自己随便建的一个普通的类调用这个静态工厂方法还会抛异常。
这个静态工厂方法的代码如下:
@CallerSensitive
public static Unsafe getUnsafe() {Class<?> caller = Reflection.getCallerClass();if (!VM.isSystemDomainLoader(caller.getClassLoader()))throw new SecurityException("Unsafe");return theUnsafe;
}
可以使用反射的方式
import sun.misc.Unsafe;
import java.lang.reflect.Field;public class TestUnsafe {private static Unsafe unsafe;static {try {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);unsafe = (Unsafe) f.get(null);} catch (Exception e) {//}}}
Unsafe功能列表
- allocateMemory/freeMemory,分配、释放堆外内存DirectMemory(和c/cpp中的malloc一样)
- CAS操作
- copyMemory
- defineClass(without security checks)
- get/put address 使用堆外内存地址进行数据的读写操作
- get/put volatile 使用堆外内存地址进行数据的读写操作 - volatile版本
- loadFence/storeFence/fullFence 禁止指令重排序
- park/unpark 阻塞/解除阻塞线程
Unsafe的数组操作
unsafe中,有两个关于数组的方法:
public native int arrayBaseOffset(Class<?> arrayClass);
public native int arrayIndexScale(Class<?> arrayClass);
arrayBaseOffset 数组第一个元素相对于数组对象起始地址的偏移量
arrayIndexScale就是指数组中每个元素所占用的空间大小,比如int[] scale就是4,long[] scale就是8,object[] scale就是4(指针大小)
// Unsafe mechanicsprivate static final sun.misc.Unsafe U;private static final long SIZECTL;private static final long TRANSFERINDEX;private static final long BASECOUNT;private static final long CELLSBUSY;private static final long CELLVALUE;private static final long ABASE;private static final int ASHIFT;static {try {U = sun.misc.Unsafe.getUnsafe();Class<?> k = ConcurrentHashMap.class;SIZECTL = U.objectFieldOffset(k.getDeclaredField("sizeCtl"));TRANSFERINDEX = U.objectFieldOffset(k.getDeclaredField("transferIndex"));BASECOUNT = U.objectFieldOffset(k.getDeclaredField("baseCount"));CELLSBUSY = U.objectFieldOffset(k.getDeclaredField("cellsBusy"));Class<?> ck = CounterCell.class;CELLVALUE = U.objectFieldOffset(ck.getDeclaredField("value"));Class<?> ak = Node[].class;ABASE = U.arrayBaseOffset(ak);int scale = U.arrayIndexScale(ak);if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);} catch (Exception e) {throw new Error(e);}}
这是CurrentHashMap中的源码我们需要注意的地方有:
Class<?> ak = Node[].class;
ABASE = U.arrayBaseOffset(ak);
ABASE:数组第一个元素相对于数组对象起始地址的偏移量 int scale = U.arrayIndexScale(ak);数组中每个元素所占用的空间大小,返回的是所占空间的字节数 scale为2的n次方,2的n次方二进制表示就是某个位置为1其余位置为0 ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); Integer.numberOfLeadingZeros(scale) 计算int型参数二进制表示数值最高位前面有几个0 由于int一共有32位,出去位1的一位剩下31位,31 - Integer.numberOfLeadingZeros(scale)就表示scale二进制表示最低位有几个连续的0
32 | ||
32位 | ||
000000000000000000000000000(27位) | 1 | 00000(5位) |
Integer.numberOfLeadingZeros() | 31 - Integer.numberOfLeadingZeros() |
@Testpublic void printObjectArrayScale() throws NoSuchFieldException, IllegalAccessException {Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");theUnsafeInstance.setAccessible(true);Unsafe U = (Unsafe) theUnsafeInstance.get(Unsafe.class);Class<?> objectArr = Object[].class;Class<?> intArr = int[].class;Class<?> longArr = long[].class;Class<?> byteArr = byte[].class;int objectScale = U.arrayIndexScale(objectArr);int intScale = U.arrayIndexScale(intArr);int longScale = U.arrayIndexScale(longArr);int byteScale = U.arrayIndexScale(byteArr);System.out.println("Object[]的sacle=" + objectScale);System.out.println("int[]的sacle=" + intScale);System.out.println("long[]的sacle=" + longScale);System.out.println("byte[]的sacle=" + byteScale);
}
可以通过上面的方法测试,引用类型的数组的scale是4,因为虽然不同对象占用内存大小不一,但是对象数组每个元素是指向对应对象的引用,即内存地址
常规的想法是通过 scale * index 来计算某个元素相对于起始位置的偏移量
@Testpublic void findArrayByBaseAndScale() throws NoSuchFieldException, IllegalAccessException {Class<Unsafe> clazz = Unsafe.class;Field unsafeField = clazz.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe)unsafeField.get(null);int[] temp = {1, 2, 3 , 4};int base = unsafe.arrayBaseOffset(int[].class);int scale = unsafe.arrayIndexScale(int[].class);System.out.println(unsafe.getInt(temp , base + 0 * scale));System.out.println(unsafe.getInt(temp , base + 1 * scale));System.out.println(unsafe.getInt(temp , base + 2 * scale));System.out.println(unsafe.getInt(temp , base + 3 * scale));
}
可以看到顺利取到了数组元素
这里看看CurrentHashMap是怎么计算元素的偏移量的
@SuppressWarnings("unchecked")static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);}
这里测试一下它的这种方法
@Testpublic void findArrayByBaseAndScale1() throws NoSuchFieldException, IllegalAccessException {Class<Unsafe> clazz = Unsafe.class;Field unsafeField = clazz.getDeclaredField("theUnsafe");unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe)unsafeField.get(null);int[] temp = {1, 2, 3 , 4};int base = unsafe.arrayBaseOffset(int[].class);int scale = unsafe.arrayIndexScale(int[].class);int ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);System.out.println("第一个元素:" + unsafe.getInt(temp , base + (0 << ASHIFT) ));System.out.println("第二个元素:" + unsafe.getInt(temp , base + (1 << ASHIFT) ));System.out.println("第三个元素:" + unsafe.getInt(temp , base + (2 << ASHIFT) ));System.out.println("第四个元素:" + unsafe.getInt(temp , base + (3 << ASHIFT) ));}
可以看到也顺利取到了数组的元素
熟悉位运算的很容易理解,其实这就是利用了一个位运算的技巧,通过向左移来实现扩大为2的n次方倍,而scale正好是2的n次方,所以可以这样计算
相关文章:

CurrentHashMap巧妙利用位运算获取数组指定下标元素
先来了解一下数组对象在堆中的存储形式【数组长度,数组元素类型信息等】 【存放元素对象的空间】 Ma 基础信息实例数据内存填充Mark Word,ClassPointer,数组长度第一个元素第二个元素固定的填充内容 所以我们想要获取某个下标的元素首先要获取这个元素的起始位置…...

实现antd designable平台的组件拖拽功能
平台:designable设计器 github:designable 目录 1 背景2 技术栈3 组件拖拽和放置3.1 类型定义3.2 拖拽3.3 放置 1 背景 由于业务需求,我们需要实现designable平台的一个简易版的组件拖拽功能。 #mermaid-svg-QrxSDGe9YyGG3LbQ {font-family:…...

计算机网络-IP组播基础
一、概述 在前面的学习交换机和路由协议,二层通信是数据链路层间通信,在同一个广播域间通过源MAC地址和目的MAC地址进行通信,当两台主机第一次通信由于不清楚目的MAC地址需要进行广播泛洪,目的主机回复自身MAC地址,然后…...

Git删除了文件拉取时失败
本地删除了一些文件,远端的另一个提交修改了被删除的文件,vs里拉取时提示未处理的提交,无法继续操作,git gui里显示很多unstaged change的项 解决办法: 1、用git bash的git rm --cached filename或 git rm -r --cached…...

【面向就业的Linux基础】从入门到熟练,探索Linux的秘密(十二)-管道、环境变量、常用命令
大致介绍了一下管道、环境变量、一些常用的基本命令,可以当作学习笔记收藏学习一下!!! 文章目录 前言 一、管道 二、环境变量 1.概念 2.查看 3.修改 4.常用环境变量 三、系统状况 总结 前言 大致介绍了一下管道、环境变量、一些常…...

Spring Boot与Apache Kafka Streams的集成
Spring Boot与Apache Kafka Streams的集成 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 一、Apache Kafka Streams简介 Apache Kafka Streams是一个用于构…...

Unity中使用VectorGraphics插件时,VectorUtils.RenderSpriteToTexture2D方法返回结果错误的解决方法
Unity中使用VectorGraphics插件时,如果使用VectorUtils.BuildSprite方法创建Sprite,那么得到的Sprite往往是一个三角网格数比较多的Sprite,如果想要得到使用贴图只有两个三角面的方形Sprite,可以使用该插件提供的VectorUtils.Rend…...

用MySQL+node+vue做一个学生信息管理系统(一):配置项目
先用npm init -y生成配置文件 在项目下新建src文件夹,app.js文件。src目录用来放静态资源文件,app.js是服务器文件,index.js是vue的入口文件 使用npm install express下载express框架 在app.js文件夹开启node服务,监听的端口为…...

2024年06月CCF-GESP编程能力等级认证Python编程二级真题解析
本文收录于专栏《Python等级认证CCF-GESP真题解析》,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(每题 2 分,共 30 分) 第 1 题 小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证…...

Unity动画系统(2)
6.1 动画系统基础2-3_哔哩哔哩_bilibili p316 模型添加Animator组件 动画控制器 AnimatorController AnimatorController 可以通过代码控制动画速度 建立动画间的联系 bool值的设定 trigger p318 trigger点击的时候触发,如喊叫,开枪及换子弹等&#x…...

深度网络现代实践 - 深度前馈网络之反向传播和其他的微分算法篇
序言 反向传播(Backpropagation,简称backprop)是神经网络训练过程中最关键的技术之一,尤其在多层神经网络中广泛应用。它是一种与优化方法(如梯度下降法)结合使用的算法,用于计算网络中各参数的…...

自动化设备上位机设计 四
目录 一 设计原型 二 后台代码 一 设计原型 二 后台代码 using SimpleTCP; using SqlSugar; using System.Text;namespace 自动化上位机设计 {public partial class Form1 : Form{SqlHelper sqlHelper new SqlHelper();SqlSugarClient dbContent null;bool IsRun false;i…...

[leetcode hot 150]第二十三题,合并K个升序链表
题目: 给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。 示例 1: 输入:lists [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:…...

MybatisPlus实现插入/修改数据自动设置时间
引言 插入数据时自动设置当前时间,更新数据时自动修改日期为修改时的日期。 使用MybatisPlus的扩展接口MetaObjectHandler 步骤 实现接口 实体类加注解 实现接口 package com.example.vueelementson.common;import com.baomidou.mybatisplus.core.handlers.M…...

Java语言程序设计篇一
Java语言概述 Java语言起源编程语言最新排名名字起源Java语言发展历程Java语言的特点Java虚拟机垃圾回收Java语言规范Java技术简介Java程序的结构Java程序注意事项:注释编程风格练习 Java语言起源 1990年Sun公司提出一项绿色计划。1992年语言开发成功最初取名为Oak…...

Calicoctl工具学习 —— 筑梦之路
官方文档: Calico Documentation | Calico Documentation 插件方式安装 calicoctl 工具 curl -o kubectl-calico -O -L "https://github.com/projectcalico/calicoctl/releases/download/v3.20.0/calicoctl"cp kubectl-calico /usr/bin/kubectl-calic…...

Wormhole Filters: Caching Your Hash on Persistent Memory——泛读笔记
EuroSys 2024 Paper 论文阅读笔记整理 问题 近似成员关系查询(AMQ)数据结构可以高效地近似确定元素是否在集合中,例如Bloom滤波器[10]、cuckoo滤波器[23]、quotient滤波器[8]及其变体。但AMQ数据结构的内存消耗随着数据规模的增长而快速增长…...

PyTorch学习之torch.transpose函数
PyTorch学习之torch.transpose函数 一、简介 torch.transpose 函数我们用于交换张量的维度。 二、语法 torch.transpose 函数用于交换给定张量的两个维度,其语法如下: torch.transpose(input, dim0, dim1)三、参数 input:待交换维度的张…...

Git仓库介绍
1. Github GitHub 本身是一个基于云端的代码托管平台,它提供的是远程服务,而不是一个可以安装在本地局域网的应用程序。因此,GitHub 不可以直接在本地局域网进行安装。 简介:GitHub是最流行的代码托管平台,提供了大量…...

人工智能笔记分享
文章目录 人工智能图灵测试分类分类与聚类的区别(重点)分类 (Classification)聚类 (Clustering) 特征提取 分类器(重点)特征提取为什么要进行特征提取?(重点)分类器 训练集、测试集大小&#x…...

秋招提前批面试经验分享(上)
⭐️感谢点开文章👋,欢迎来到我的微信公众号!我是恒心😊 一位热爱技术分享的博主。如果觉得本文能帮到您,劳烦点个赞、在看支持一下哈👍! ⭐️我叫恒心,一名喜欢书写博客的研究生在读…...

[AIGC] ClickHouse的表引擎介绍
ClickHouse是一种高性能的列式数据库管理系统,支持各种不同的表引擎。表引擎是数据库系统中的核心组件,它定义了数据的存储方式和访问方式。本文将介绍ClickHouse中常见的表引擎及其特点。 文章目录 一、MergeTree引擎二、ReplacingMergeTree引擎三、Sum…...

关于新装Centos7无法使用yum下载的解决办法
起因 之前也写了一篇类似的文章,但感觉有漏洞,这次想直接把漏洞补齐。 问题描述 在我们新装的Centos7中,如果想要用C编程,那就必须要用到yum下载,但是,很多新手,包括我使用yum下载就会遇到一…...

OpenEarthMap:全球高分辨率土地覆盖制图的基准数据集(开源来下载!!!)
OpenEarthMap由220万段5000张航拍和卫星图像组成,覆盖6大洲44个国家97个地区,在0.25-0.5m的地面采样距离上人工标注8类土地覆盖标签。我们提供8类标注:裸地、牧场、已开发空间、道路、树木、水、农业用地和建筑。类选择与现有的具有亚米GSD的产品和基准数…...

工作助手VB开发笔记(1)
1.思路 1.1 样式 样式为常驻前台的一个小窗口,小窗口上有三到四个按钮,为一级功能,是当前工作内容的常用功能窗口,有十个二级窗口,为选中窗口时的扩展选项,有若干后台功能,可选中至前台 可最…...

WAWA鱼曲折的大学四年回忆录
声明:本文内容纯属个人主观臆断,如与事实不符,请参考事实 前言: 早想写一下大学四年的总结了,但总是感觉无从下手,不知道从哪里开始写,通过这篇文章主要想做一个记录,并从现在的认…...

Go 依赖注入设计模式
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

使用React复刻ThreeJS官网示例——keyframes动画
最近在看three.js相关的东西,想着学习一下threejs给的examples。源码是用html结合js写的,恰好最近也在学习react,就用react框架学习一下。 本文参考的是threeJs给的第一个示例 three.js examples (threejs.org) 一、下载threeJS源码 通常我们…...

嵌入式linux面试1
1. linux 1.1. Window系统和Linux系统的区别 linux区分大小写windows在dos(磁盘操作系统)界面命令下不区分大小写; 1.2. 文件格式区分 windows用扩展名区分文件;如.exe代表执行文件,.txt代表文本文件,.…...

智能交通(3)——Learning Phase Competition for Traffic Signal Control
论文分享 https://dl.acm.org/doi/pdf/10.1145/3357384.3357900https://dl.acm.org/doi/pdf/10.1145/3357384.3357900 论文代码 https://github.com/gjzheng93/frap-pubhttps://github.com/gjzheng93/frap-pub 摘要 越来越多可用的城市数据和先进的学习技术使人们能够提…...