【api源码共存平台】【JAVA包网源码】【织梦导购源码】map集合源码分享视频_map集合原理

来源:fsr源码

1.走进SWMM源代码——GIS转SWMM经验及工具分享
2.宏观看 Go 语言中的集集合 Map 内部
3.手把手带你学webpack(6)--source-map
4.如何设计并实现一个线程安全的 Map
5.MapReduce源码解析之Mapper
6.记一次源码追踪分析,从Java到JNI,合源再到JVM的码分C++:fileChannel.map()为什么快;源码分析map方法,put方法

map集合源码分享视频_map集合原理

走进SWMM源代码——GIS转SWMM经验及工具分享

       作者:赵也(深圳创环)

       GIS格式数据在城市管线数据储存、享视智慧水务平台等行业中广泛应用。原理PINS等工具提供简单处理方式,集集合api源码共存平台但复杂转换需要二次开发。合源本文从Gis二次开发、码分UI使用、享视转换算法编写角度,原理介绍GIS转SWMM流程。集集合

       欢迎关注“市政规划交流”公众号。合源

       视频主要内容:

       Part1:Gis二次开发

       基于ArcMap控件二次开发。码分环境配置:ArcGis 享视for Desktop.1、ArcObject SDK for Microsoft .Net Framework4.0、原理Microsoft Visual Studio (.4 + 4.5 +也行)

       Part2:UI简介

       建立Gis和SWMM文件桥梁,简化数据转换流程。

       Part3:核心算法模块介绍

       Step1:背景数据需求。基础排水管网模型需空间数据包括:

       点要素:检查井/排放口,包含编号、底高程、地表高程、标识字段等。

       线要素:排水管渠,包含编号、起点编号、终点编号、断面类型、断面参数等。

       面要素:汇水区,JAVA包网源码包含编号、汇流编号、不透水率等。

       Step2:解析GIS点、线、面要素生成Inp文件结构。

       核心代码模块介绍:

       定义输出字符串集合,Inp文件本质是ASCII文件,字符串集合表示文件内容。

       遍历要素集,通过FeatureCursor光标遍历特定要素(管网、检查井、下垫面),提取具体字符串。

       将前缀字符串和提取的字符串依次写入Inp文件。

       Part4:案例实操/工具分享

       视频分左右,展示使用工具生成可运行的Inp文件案例。

       额外提及:软件包中ConvertSWMMTest.esriaddin文件,为无编程环境的用户提供了直接安装addin并使用ConvertSwmmTest工具的途径。

       附录:INP文件结构介绍

宏观看 Go 语言中的 Map 内部

       在 Go 语言的世界里,关于 map 的深入理解相对较少,这与slice的热门探讨形成鲜明对比。为了揭开 map 的神秘面纱,我查阅了其源码,尽管代码复杂,但我们可以从宏观视角解析 map 的构建与增长机制,从而理解其为何高效快速且无序。

       首先,创建和使用 map 的织梦导购源码基本操作是通过指定 key 储存 value,通过 key 可直接访问 value,而且遍历 map 时,key 的顺序并非插入时的顺序,每次运行代码,key 的顺序都会有所变化。

       深入来看,Go 语言中的 map 实质上是基于散列表(hash table)实现的,由一系列桶(bucket)组成,桶的数量是 2 的幂。插入键值对时,会根据 key 生成散列值并根据其低阶位决定放入哪个桶。桶内部包含一个数组和一个 byte 数组,数组用于存储键值对,而 byte 数组则是为了节省内存对齐而设计的。

       当 map 需要增长时,会根据装载阈值决定,这涉及内存分配、旧桶数据迁移等操作,以保证迭代器在增长过程中仍能正常工作。虽然代码使用 C 编写,但理解这些细节有助于我们更有效地使用 map,尤其是对于预知 key 数量的场景。

       最后,感谢 Stephen McQuay 和 Keith Randall 的审阅和指正。本文由 William Kennedy 编写,由 maxwell 译成中文,由 GCTT 原创编译,并由 Go语言中文网推出。

手把手带你学webpack(6)--source-map

       本篇文章对应源码:JvcicpO1xuXG4gIHRocmIG5ldyBFcnJvcignctZXRoaW5nIHdybnLi4uJyk7XGXG5cbm1vZHVsZS5leHBvcnRzID0geyBlcnJvckZuIHXG4iXSwibmFtZXMiOltdLCJzb3VyY2VSbIjoiIn0=\n//#sourceURL=webpack-internal:///./src/utils.js\n");

       这种方式适用于在开发模式下需要精确的MDUI网盘源码source-map时使用,相比直接的eval,会更加精确些

3.4inline-source-map

       顾名思义,就是以内联方式存放source-map文件,它会将source-map文件的内容编码成base后直接放在打包结果的最后

constHtmlWebpackPlugin=require('html-webpack-plugin');const{ CleanPlugin}=require('webpack');/***@type{ import('webpack').Configuration}*/module.exports={ mode:'development',devtool:'inline-source-map',plugins:[newHtmlWebpackPlugin(),newCleanPlugin()],};//#sourceMappingURL=data:application/json;charset=utf-8;base,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUEsbUJBQW1COzs7Ozs7O1VDTm5CO1VBQ0E7O1VBRUE7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7O1VBRUE7VUFDQTs7VUFFQTtVQUNBO1VBQ0E7Ozs7Ozs7OztBQ3RCQSxRQUFRLFVBQVUsRUFBRSxtQkFBTyxDQUFDLCtCQUFTOztBQUVyQyIsInNvdXJjZXMiOlsid2VicGFjazovLzA2X3dlYnBhY2tfccmNlXhcC8uL3NyYydGlscy5qcyIsIndlYnBhY2s6Ly8wNlZWJwYWNrX3NvdXJjZV9tYXAvd2VicGFjay9ibc3RyYXAiLCJ3ZWJwYWNrOi8vMDZfd2VicGFjazb3VyY2VfbWFwLy4vc3JjL2luZGV4LmpzIl0sInNvdXJjZXNDbZWIjpbImZ1bmN0aW9uIGVycm9yRm4oKSB7XG4gIGNvbnNvbGUubG9nKCdoZWxsbyBlcnJvcicpO1xuXG4gIHRocmIG5ldyBFcnJvcignctZXRoaW5nIHdybnLi4uJyk7XGXG5cbm1vZHVsZS5leHBvcnRzID0geyBlcnJvckZuIHXG4iLCIvLyBUaGUgbW9kdWxlIGNhY2hlXGYXIgXZWJwYWNrXvZHVsZV9jYWNoZV9fID0geXG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gXZWJwYWNrXvZHVsZV9jYWNoZV9fWvZHVsZUlkXTtcblWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaWbyB0aGUgY2FjaGUpXG5cdHZhciBtb2R1bGUgPSBfX3dlYnBhY2tfbW9kdWxlX2NhY2hlXbbW9kdWxlSWRdID0ge1xuXHRcdC8vIG5vIG1vZHVsZS5pZCBuZWVkZWRcblx0XHQvLyBubyBtb2R1bGUubG9hZGVkIG5lZWRlZFxuXHRcdGV4cG9ydHM6IHt9XG5cdHXG5cblx0Ly8gRXhlY3V0ZSB0aGUgbW9kdWxlIGZ1bmN0aW9uXG5cdF9fd2VicGFjatb2R1bGVzXbbW9kdWxlSWRdKG1vZHVsZSwgbW9kdWxlLmV4cG9ydHMsIF9fd2VicGFjayZXF1aXJlXpO1xuXG5cdC8vIFJldHVybiB0aGUgZXhwb3J0cyBvZiB0aGUgbW9kdWxlXG5cdHJldHVybiBtb2R1bGUuZXhwb3J0cztcbn1cblxuIiwiYuc3QgeyBlcnJvckZuIH0gPSByZXF1aXJlKCcuL3V0aWxzJyk7XG5cbmVycm9yRm4oKTtcbiJdLCJuYW1lcyI6WsInNvdXJjZVJvb3QiOiIifQ==

       从官方文档可以看到,这种方式的构建速度是最慢的,只适用于构建单个文件的时候使用

3.5cheap-source-map

       这种方式相比source-map而言,没有建立列映射,也就是说遇到报错的时候,只会告诉你哪一行代码出错了,而不会告诉你哪一列出错了,如果开发时对列映射没有太高要求的话可以使用这种方式,毕竟不用生成列映射,比起source-map来说会快一些

constHtmlWebpackPlugin=require('html-webpack-plugin');const{ CleanPlugin}=require('webpack');/***@type{ import('webpack').Configuration}*/module.exports={ mode:'development',devtool:'cheap-source-map',plugins:[newHtmlWebpackPlugin(),newCleanPlugin()],};3.6cheap-module-source-map

       官方文档对这种方式的devtool并没有进行任何详细介绍,事实上这种方式适用于js代码被loader转换过的场景,比如被babel进行了转换,又比如源码是用typescript写的,后来经过loader转成了js代码,而我们又希望在运行的时候出现报错信息时能够对应回typescript代码像这种有loader对js进行转换的场景下,想要保证正确的source-map就需要使用到带有module的devtool了,因为除了cheap-module-source-map,还有很多别的方式也是有module的,只要是在官方文档中看到带有module的devtool都是具有这种特性

       下面就以babel为例,我们通过babel-loader对js进行转换,然后看看能否正确对应到转换前的代码首先安装如下依赖

pnpmi@babel/core@babel-preset-envbabel-loader

       @babel/core是babel的核心,所有功能都要在这个包的基础上运行

       @babel-preset-env让我们可以不需要考虑转换成什么版本的js,它会根据要适配的浏览器自动转换成能兼容相应浏览器的版本,这里我们使用它主要是能够将我们写的es6代码转成es5,从而让我们的源码和打包后的结果有差异,方便观察source-map是iapp引入html源码否生效

       babel-loader,用于和webpack搭配使用,转换js文件

       接下来配置loader

constHtmlWebpackPlugin=require('html-webpack-plugin');const{ CleanPlugin}=require('webpack');/***@type{ import('webpack').Configuration}*/module.exports={ mode:'development',devtool:'eval',//默认就是eval,因此development模式下不写devtool配置项也可以plugins:[newHtmlWebpackPlugin(),newCleanPlugin()],};0

       然后我们写一个具有es6特性的语法的函数

constHtmlWebpackPlugin=require('html-webpack-plugin');const{ CleanPlugin}=require('webpack');/***@type{ import('webpack').Configuration}*/module.exports={ mode:'development',devtool:'eval',//默认就是eval,因此development模式下不写devtool配置项也可以plugins:[newHtmlWebpackPlugin(),newCleanPlugin()],};1

       使用到了const、箭头函数,经过babel转换成es5后,代码的位置会和源码中不一样,那么在浏览器中如果仍然能够找到转换前的源码,则说明cheap-module-source-map生效了可以看到,在浏览器中确实能够看到转换前的源码,这就是cheap-module-source-map中module的作用,事实上官方文档中这么多的配置项我们不需要害怕,只需要知道每个关键字是什么意思,那么它们组合起来无非就是各种特性的叠加而已

3.7hidden-source-map

       也是一个见名知意的配置项,相比于source-map,就是将最后的//#sourceMappingURL=main.js.map这句注释删除了,这也就意味着source-map不会生效了,但是仍然会生成source-map文件的官方文档中给我们的建议是在只需要知道有错误出现时给我们在控制台输出出来的话就可以使用这种方式

3.8nosources-source-map

       这种方式能够在出现错误的时候告诉我们是源码中哪个文件第几行出错了,但是不会在浏览器中给我们生成源码

总结

       了解完以上这几个devtool配置项,就足够了,官网的个配置项就是根据eval、hidden、inline、cheap、module、nosources这几个关键字组合出来的

       但是组合也是有规则的,官方文档中给出的规则如下:

       [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

原文:/post/

如何设计并实现一个线程安全的 Map

       Map 是一个非常常用的数据结构,一个无序的 key/value 对的集合,其中 Map 所有的 key 都是不同的,然后通过给定的 key 可以在常数时间 O(1) 复杂度内查找、更新或删除对应的 value。

       è¦æƒ³å®žçŽ°å¸¸æ•°çº§çš„查找,应该用什么来实现呢?读者应该很快会想到哈希表。确实,Map 底层一般都是使用数组来实现,会借用哈希算法辅助。对于给定的 key,一般先进行 hash 操作,然后相对哈希表的长度取模,将 key 映射到指定的地方。

       å“ˆå¸Œç®—法有很多种,选哪一种更加高效呢?

       1. 哈希函数

       MD5 和 SHA1 可以说是目前应用最广泛的 Hash 算法,而它们都是以 MD4 为基础设计的。

       MD4(RFC ) 是 MIT 的Ronald L. Rivest 在 年设计的,MD 是 Message Digest(消息摘要) 的缩写。它适用在位字长的处理器上用高速软件实现——它是基于 位操作数的位操作来实现的。

       MD5(RFC ) 是 Rivest 于年对 MD4 的改进版本。它对输入仍以位分组,其输出是4个位字的级联,与 MD4 相同。MD5 比 MD4 来得复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。

       SHA1 是由 NIST NSA 设计为同 DSA 一起使用的,它对长度小于的输入,产生长度为bit 的散列值,因此抗穷举 (brute-force)

       æ€§æ›´å¥½ã€‚SHA-1 设计时基于和 MD4 相同原理,并且模仿了该算法。

       å¸¸ç”¨çš„ hash 函数有 SHA-1,SHA-,SHA-,MD5 。这些都是经典的 hash 算法。在现代化生产中,还会用到现代的 hash 算法。下面列举几个,进行性能对比,最后再选其中一个源码分析一下实现过程。

       ï¼ˆ1) Jenkins Hash 和 SpookyHash

       å¹´ Bob Jenkins 在《 Dr. Dobbs Journal》杂志上发表了一片关于散列函数的文章《A hash function for hash Table lookup》。这篇文章中,Bob 广泛收录了很多已有的散列函数,这其中也包括了他自己所谓的“lookup2”。随后在年,Bob 发布了 lookup3。lookup3 即为 Jenkins Hash。更多有关 Bob’s 散列函数的内容请参阅维基百科:Jenkins hash function。memcached的 hash 算法,支持两种算法:jenkins, murmur3,默认是 jenkins。

       å¹´ Bob Jenkins 发布了他自己的一个新散列函数

       SpookyHash(这样命名是因为它是在万圣节发布的)。它们都拥有2倍于 MurmurHash 的速度,但他们都只使用了位数学函数而没有位版本,SpookyHash 给出位输出。

       ï¼ˆ2) MurmurHash

       MurmurHash 是一种非加密型哈希函数,适用于一般的哈希检索操作。

       Austin Appleby 在年发布了一个新的散列函数——MurmurHash。其最新版本大约是 lookup3 速度的2倍(大约为1 byte/cycle),它有位和位两个版本。位版本只使用位数学函数并给出一个位的哈希值,而位版本使用了位的数学函数,并给出位哈希值。根据Austin的分析,MurmurHash具有优异的性能,虽然 Bob Jenkins 在《Dr. Dobbs article》杂志上声称“我预测 MurmurHash 比起lookup3要弱,但是我不知道具体值,因为我还没测试过它”。MurmurHash能够迅速走红得益于其出色的速度和统计特性。当前的版本是MurmurHash3,Redis、Memcached、Cassandra、HBase、Lucene都在使用它。

       ä½œè€…:一缕殇流化隐半边冰霜

MapReduce源码解析之Mapper

       MapReduce,大数据领域的标志性计算模型,由Google公司研发,其核心概念"Map"与"Reduce"简明易懂却威力巨大,打开了大数据时代的大门。对于许多大数据工作者来说,MapReduce是基础技能之一,而源码解析更是深入理解与实践的必要途径。

       MapReduce由两部分组成:Map与Reduce。Map阶段通过映射函数将一组键值对转换成另一组键值对,而Reduce阶段则负责合并这些新的键值对。这种并行计算模型极大地提高了大数据处理的效率。

       本文将聚焦于Map阶段的核心实现——Mapper。通过解析Mapper类及其子类的源码,我们可以更深入地理解MapReduce的工作机制,并在易观千帆等技术数据处理中发挥更大的效能。

       Mapper类内部包含四个关键方法与一个抽象类:

       setup():主要为map()方法做准备,例如加载配置文件、传递参数。

       cleanup():用于清理资源,如关闭文件、处理Key-Value。

       map():程序的逻辑核心,对输入的文本进行处理(如分割、过滤),以键值对的形式写入context。

       run():驱动Mapper执行的主方法,按照预设顺序执行setup()、map()、cleanup()。

       Context抽象类扮演着重要角色,用于跟踪任务状态和数据存储,如在setup()中读取配置信息,并作为Key-Value载体。

       下面是几个Mapper子类的详细解析:

       InverseMapper:将键值对反转,适用于不同需求的统计分析。

       TokenCounterMapper:使用StringTokenizer对文本进行分割,计算特定token的数量,适用于词频统计等。

       RegexMapper:对文本进行正则化处理,适用于特定格式文本的统计。

       MultithreadedMapper:利用多线程执行Mapper任务,提高CPU利用率,适用于并发处理。

       本文对MapReduce中Mapper及其子类的源码进行了详尽解析,旨在帮助开发者更深入地理解MapReduce的实现机制。后续将探讨更多关键类源码,以期为大数据处理提供更深入的洞察与实践指导。

记一次源码追踪分析,从Java到JNI,再到JVM的C++:fileChannel.map()为什么快;源码分析map方法,put方法

       前言

       在系统IO相关的系统调用有read/write,mmap,sendfile等这些。

       其中read/write是普通的读写,每次都需要将buffer从用户空间拷贝到内核空间;

       而mmap使用的是内存映射,会将磁盘文件对应的页映射(拷贝)到内核空间的page cache,并记录到用户进程的页表中,使得用户空间也可以像操作用户空间一样操作该文件的映射,最后再由操作系统来讲该映射(脏页)回写到磁盘;

       sendfile则使用的是零拷贝技术,在mmap的基础上,当发送数据的时候只拷贝fd和offset等元数据信息,而将数据主体直接拷贝至protocol buffer,实现了内核数据零冗余的零拷贝技术

       本文地址:/post//

问题/目的问题1Java中哪些API使用到了mmap问题2怎么知道该API使用到了mmap,如何追踪程序的系统调用目的1源码中分析验证,从Java到JNI,再到C++:fileChannel.map()使用的是系统调用mmap目的2源码验证分析:调用mmapedByteBuffer.put(Byte[])时JVM在搞些什么?mmap比普通的read/write快在哪?揭晓答案1mmap在Java NIO中的体现/使用

       看一个例子

// 1GBpublic static final int _GB = 1**;File file = new File("filename");FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();MappedByteBuffer mmapedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB);for (int i = 0; i < _GB; i++) { count++;mmapedByteBuffer.put((byte)0);}

       其中fileChannel.map()底层使用的就是系统调用mmap,函数签名为: public abstract MappedByteBuffer map(MapMode mode,long position, long size)throws IOException

答案2程序执行的系统调用追踪/** * @author Tptogiar * @description * @date /5/ - : */public class TestMappedByteBuffer{ public static final int _4kb = 4*;public static final int _GB= 1**;public static void main(String[] args) throws IOException, InterruptedException { // 为了方便在日志中找到本段代码的开始位置和结束位置,这里利用文件io来打开始标记FileInputStream startInput = null;try { startInput = new FileInputStream("start1.txt");startInput.read();} catch (IOException e) { e.printStackTrace();}File file = new File("filename");FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB); //我们想分析的语句问题2for (int i = 0; i < _GB; i++) { map.put((byte)0); // 下文中需要分析的语句目的2}// 打结束标记FileInputStream endInput = null;try { endInput = new FileInputStream("end.txt");endInput.read();} catch (IOException e) { e.printStackTrace();}}}

       把上面这段代码编译后把“.class”文件拉到linux执行,并用linux上的strace工具记录其系统调用日志,拿到日志文件我们可以在日志中看到以下信息(关于怎么拿到日志可以参照我的博文:无(代写)):

       注:日志有多行,这里只选取我们关注的

// ...// 看到了我们打的开始标志openat(AT_FDCWD, "start1.txt", O_RDONLY) = -1 ENOENT (No such file or directory)// ... // 打开文件,文件描述符fd为6openat(AT_FDCWD, "filename", O_RDWR|O_CREAT, ) = 6// 判断文件状态fstat(6, { st_mode=S_IFREG|, st_size=, ...}) = 0// ... // 判断文件状态fstat(6, { st_mode=S_IFREG|, st_size=, ...}) = 0// 进行内存映射mmap(NULL, , PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x7f2fd6cd// ...// 程序退出exit(0)// 看到了我们打的结束标志openat(AT_FDCWD, "end.txt", O_RDONLY) = -1 ENOENT (No such file or directory)

       在上面程序的系统调用日志中我们确实看到了我们打的开始标志,结束标志。在开始标志和结束标志之间我们看到了我们的文件"filename"确实被打开了,文件描述符fd = 6;在打开文件后紧接着又执行了系统调用mmap,这一点我们Java代码一致,这样,我们就验证了我们答案1中的结论,可以开始我们的下文了

源码追踪分析,从Java到JNI,再到JVM的C++目的1寻源之旅:fileChannel.map()

       我们知道我们执行Java代码fileChannel.map()确实会在底层调用系统调用,那怎么在源码中得到验证呢?怎么落脚于源码进行分析呢?下面开始我们的寻源之旅

       FileChannelImpl.map() 注:由于代码较长,这里代码中略去了一些我们不关注的,比如异常捕获等

public MappedByteBuffer map(MapMode mode, long position, long size)throws IOException{ // ...try { // ...synchronized (positionLock) { // ...long mapPosition = position - pagePosition;mapSize = size + pagePosition;try { // !我们要找的语句就在这!addr = map0(imode, mapPosition, mapSize);} catch (OutOfMemoryError x) { // 如果内存不足,先尝试进行GCSystem.gc();try { Thread.sleep();} catch (InterruptedException y) { Thread.currentThread().interrupt();}try { // 再次试着mmapaddr = map0(imode, mapPosition, mapSize);} catch (OutOfMemoryError y) { // After a second OOME, failthrow new IOException("Map failed", y);}}} // ...} finally { // ...}}

       上面函数源码中真正执行mmap的语句是在addr = map0(imode, mapPosition, mapSize),于是我们寻着这里继续追踪

       FileChannelImpl.map0()

// Creates a new mappingprivate native long map0(int prot, long position, long length)throws IOException;

       可以看到,该方法是一个native方法,所以后面的源码我们需要到这个FileChannelImpl.class对应的fileChannelImpl.c中去看,所以我们需要去找到JDK的源码

       在JDK源码中我们找到fileChannelImpl.c文件

       fileChannelImpl.c 根据JNI的对应规则,我们找到该文件内对应的Java_sun_nio_ch_FileChannelImpl_map0方法,其源码如下:

JNIEXPORT jlong JNICALLJava_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this, jint prot, jlong off, jlong len){ void *mapAddress = 0;jobject fdo = (*env)->GetObjectField(env, this, chan_fd);jint fd = fdval(env, fdo);int protections = 0;int flags = 0;if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { protections = PROT_READ;flags = MAP_SHARED;} else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { protections = PROT_WRITE | PROT_READ;flags = MAP_SHARED;} else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { protections =PROT_WRITE | PROT_READ;flags = MAP_PRIVATE;}// !我们要找的语句就在这里!mapAddress = mmap(0,/* Let OS decide location */len,/* Number of bytes to map */protections,/* File permissions */flags,/* Changes are shared */fd, /* File descriptor of mapped file */off); /* Offset into file */if (mapAddress == MAP_FAILED) { if (errno == ENOMEM) { JNU_ThrowOutOfMemoryError(env, "Map failed");return IOS_THROWN;}return handle(env, -1, "Map failed");}return ((jlong) (unsigned long) mapAddress);}

       我们要找的语句就上面代码中的mapAddress = mmap(0,len,protections,flags,fd,off),至于为什么不是直接的mmap,而是mmap,是因为这里的mmap是一个宏,在文件上方有其定义,如下:

#define mmap mmap

       至此,我们就在源码中得到验证了我们问题2中的结论:fileChannelImpl.map()底层使用的是mmap系统调用

目的2寻源之旅:mmapedByteBuffer.put(Byte[ ])

       接着我们来看看当我们调用mmapedByteBuffer.put(Byte[])JVM底层在搞些什么动作

       MappedByteBuffer ?首先我们得知道,当我们执行MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB)时,实际返回的对象是DirectByteBuffer类的实例,因为MappedByteBuffer为抽象类,且只有DirectByteBuffer继承了它,看下面两图就明白了

       DirectByteBuffer 于是我们找到DirectByteBuffer内的put(Byte[ ])方法

public ByteBuffer put(byte x) { unsafe.putByte(ix(nextPutIndex()), ((x)));return this;}

       可以看到该方法内实际是调用Unsafe类内的putByte方法来实现功能的,所以我们还得去看Unsafe类

       Unsafe.class

public native voidputByte(long address, byte x);

       该方法在Unsafe内是一个native方法,所以所以我们还得去看unsafe.cpp文件内对应的实现

       unsafe.cpp

       在JDK源码中,我们找到unsafe.cpp

       在这份源码内,没有使用JNI内普通加前缀的方法来形成对应关系

       不过我们还是能顺着源码的蛛丝轨迹找到我们要找的方法

       注意到源码中有这样的注册机制,所以我们可以知道我们要找的代码就是上图中标注的代码

       顺藤摸瓜,我们就找到了该方法的定义

UNSAFE_ENTRY(void, Unsafe_SetNative##Type(JNIEnv *env, jobject unsafe, jlong addr, java_type x)) \UnsafeWrapper("Unsafe_SetNative"#Type); \JavaThread* t = JavaThread::current(); \t->set_doing_unsafe_access(true); \void* p = addr_from_java(addr); \*(volatile native_type*)p = x; \t->set_doing_unsafe_access(false); \UNSAFE_END \

       该方法内主要的逻辑语句就是以下两句:

/** * @author Tptogiar * @description * @date /5/ - : */public class TestMappedByteBuffer{ public static final int _4kb = 4*;public static final int _GB= 1**;public static void main(String[] args) throws IOException, InterruptedException { // 为了方便在日志中找到本段代码的开始位置和结束位置,这里利用文件io来打开始标记FileInputStream startInput = null;try { startInput = new FileInputStream("start1.txt");startInput.read();} catch (IOException e) { e.printStackTrace();}File file = new File("filename");FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB); //我们想分析的语句问题2for (int i = 0; i < _GB; i++) { map.put((byte)0); // 下文中需要分析的语句目的2}// 打结束标记FileInputStream endInput = null;try { endInput = new FileInputStream("end.txt");endInput.read();} catch (IOException e) { e.printStackTrace();}}}0

       至此,我们就知道:其实我们调用mmapedByteBuffer.put(Byte[ ])时,JVM底层并不需要涉及到系统调用(这里也可以用strace工具追踪从而得到验证)。也就是说通过mmap映射的空间在内核空间和用户空间是共享的,我们在用户空间只需要像平时使用用户空间那样就行了————获取地址,设置值,而不涉及用户态,内核态的切换

总结

       fileChannelImpl.map()底层用调用系统函数mmap

       fileChannelImpl.map()返回的其实不是MappedByteBuffer类对象,而是DirectByteBuffer类对象

       在linux上可以通过strace来追踪系统调用

       JNI中“.class”文件内方法与“.cpp”文件内函数的对应关系不止是前缀对应的方法,还可以是注册的方式,这一点的追寻代码的时候有很大帮助

       directByteBuffer.put()方法底层并没有涉及系统调用,也就不需要涉及切态的性能开销(其底层知识执行获取地址,设置值的操作),所以mmap的性能就比普通读写read/write好

       ...

原文:/post/

MapBox源码解读 - style 的加载逻辑

       探讨MapBox源码中的style加载逻辑,从数据源到图层显示的变化过程。

       style的加载涉及数据请求、解析、属性赋值、着色器执行等多个环节,演示通过示例代码进行定制化开发。

       style加载和渲染流程包括请求、解析、数据赋值、渲染等步骤,详细内容见附件。

       实现单侧线绘制效果,具体实现代码如下所示。

       总结今日讲解内容,分享一个web端调试webgl的插件——Spector.js,Firefox和Chrome浏览器皆可使用,自行下载并探索其功能。

文章所属分类:探索频道,点击进入>>