- 博客(28)
- 问答 (1)
- 收藏
- 关注
原创 JVM反射的实现
实现方式有两种不同的实现方式,一种是本地实现,一种是动态实现。JVM开始运行之后,方法的代码和入口地址都能获取到,想要通过反射调用方法,无非就是找到方法地址,然后将参数传递进去执行。本地实现就是使用native方法直接调用方法。但是这种方法涉及到java代码调用c++代码,单次调用性能较差。动态实现是指通过字节码工具生成一个中间类,这个中间类直接调用目标方法,因此单次调用性能跟直接调用基本没有区别。但是生成动态类,以及动态类字节码的解释执行在首次执行是耗时很长。所以jvm对于调用次数很少的方法仍然
2021-06-09 15:22:11
168
原创 java如何处理异常
异常的继承关系所有异常都是继承自Throwable类及其子类,Throwable类有两个主要的子类,一个是Error一个是Exception,语义上来说Error比较严重,表示当前程序执行不下去也无法恢复。Exception下有一个子类叫RuntimeException,RuntimeException和Error是非检查异常,即不需要显示的处理和接收的异常,其他异常则必须进行显示的try-catch处理或重新throw出去。异常的处理模块try模块:需要监控的代码块,其中若产生了异常则交给catch
2021-06-09 15:19:56
169
原创 java方法调用及句柄
java方法调用invokeStatic:调用静态方法invokeSpecial: 调用非静态私有方法、super方法、构造函数invokeVirtual:调用非静态公开方法invokeInterface:调用接口方法invokeDynamic:动态方法调用前两个是静态绑定,运行时能知道具体的方法地址;后三个是动态绑定,运行时需要根据实际调用对象来寻找正确的方法,以方法表的方式实现。final标识的方法是不允许重写的,因此也可以静态绑定。方法表分为虚方法表和接口方法表,分别对应invokeVi
2021-06-09 15:17:53
432
原创 java垃圾回收及对象创建
安全点、安全区域JVM垃圾回收线程工作的时候往往需要stop the world(stw),即停止正在运行中用户线程。因为JVM垃圾回收是基于可达性分析,可达性分析第一步首先就是要找到能标识对象仍可到达的GC Root,包括(方法栈中的局部变量,方法区的静态变量、常量池中的引用、本地方法中的引用等),如果用户线程一直在运行的话,GC root集合可能不停在变化,因此必须使得用户线程能够暂时停止改变GC root集合。为了实现这个目的,JVM在启动垃圾回收之后会通知所有的用户线程进去安全点或安全区域。如果
2021-06-09 15:11:43
156
原创 分布式锁实现
分布式锁锁的本质是一个所有线程或进程都可以看见的共享变量,这个共享变量具有互斥性,当一个线程或进程拥有这个变量之后,其他线程或进程就只能阻塞,直到变量被释放,再重新去竞争。拥有了共享变量即获取了锁,只有获取锁之后才能执行互斥区中的代码。对于单机应用来说,代表锁的共享变量只要存在于单机内存中即可保证所有的线程和进程可见。例如java synchronize实现中基于对象头中的mark字段,lock接口中是使用int值来存储锁状态的变更,使用cas来更新状态,并使用volatile来保证int值更新后的可见
2021-06-09 14:41:40
69
原创 kafka协调者
协调者角色协调者本身是kafka集群的一个broker,负责一个或多个消费者组的消费位移offset提交以及Rebalance工作。如何确定协调者?协调者所在分区:消费者组groupID的哈希值 % _consumer_offsets队列分区数量步骤1确定的分区的leader副本所在broker即为协调者。消费位移提交消费者本地会维护一个消费位移offset,每次从kafka队列中拉取消息时都会使用该offset,消费成功之后更新offset。但是消费者组中不同消费者实例在执行过程中可能因为R
2021-06-09 14:40:26
773
原创 LRU算法在redis和mysql中的不同应用
LRU算法用来做数据淘汰的算法,用来淘汰最近最少访问的数据;一般使用链表实现,当一个key被访问时,就将key放在链表头部,当需要淘汰数据时,就将链表末尾的数据淘汰。redis中的应用redis中淘汰数据有两个方式:定期删除:redis有后台线程,会定时扫描数据,从中选择应该淘汰的过期数据将其删除。假如每次删除都对所有的key进行一次最近访问时间排序的话,对性能消耗非常大,redis采用的是随机抽样的方式进行删除,例如LRU算法删除,则随机抽取20个key,从中找出最近未访问的key进行删除。这样随
2021-06-09 14:36:39
213
原创 LSM存储引擎
LSM(log structured merge Trees)是一种存储引擎,存储引擎有两个最基本的操作就是读和写。LSM之所以出现的根本原因就在于磁盘的顺序读写性能远远高于随机读写。问题对于存储引擎来说,如果只是写而不考虑读的话就完全不需要有那么多花里胡哨的设计,只需要保证写基本是顺序写就可以。最简单的实现就是在内存中加个缓冲区,当缓冲区中的数据满时,刷新到磁盘上。但是要保证数据的快速读取,就必须要知道每一条数据具体存储在文件的什么位置,或者大概位置。在没有索引的情况下,只能对整个文件进行遍历和匹配,
2021-05-31 11:48:33
930
原创 大数据笔记
行式存储与列式存储传统的关系型数据库例如mysql,是使用行式数据存储,所有的记录是以行为单位,一个数据块旁边是同一条记录的另一个字段的数据块。idnameage1tiang182ling194tian21这样的优点在于:每次读取可以很方便的读取整行数据,适用于查询条件或查询结果涉及到所有字段的访问。以行为单位,可以很方便保证行级数据的一致性,比较容易实现事务处理但是行式数据库也有缺点,当一个数据库表中字段数量太多,例如有一百列,每次查询只访
2021-05-18 20:58:06
187
1
原创 ZK的脑裂和选主
基础概念zxid:zk中的每一个更新操作都被封装成一个事务操作proposal,zxid是事务的唯一标识myid:zk中的每一台服务器都有自己的唯一id标识epoch:年代,leader每更换一次,年代值就加一ZK节点的状态:LOOKING: 处于选举状态;LEADING: 处于领导者状态;FOLLOWING:处于追随者状态;OBSERVING:处于观察者状态,此状态下的机器无权利投票ZK的选主流程ZK集群中只能有一个leader服务器,其他的服务器为follower或observe
2021-05-07 16:30:22
831
原创 redis分片集群的哈希策略
redis的分片集群采用哈希的策略进行,由哈希值决定请求交予哪个具体的服务器处理。假设有三台服务器,[0, 1, 2];使用哈希函数,例如取模,hash = key % 3;得到key的哈希值之后,将key发送到对应的服务器中处理。由于key是完全无规律的,当其中一台服务器挂掉之后,hash变成hash = key%2;这样几乎所有key的哈希值都会改变,也就导致几乎所有key的缓存都失效了,造成缓存雪崩。根本原因就是因为一个服务器宕机之后,哈希函数发生了变化(由之前的key%3变成了key%2),所
2021-05-07 16:20:27
412
原创 从缓存失效谈起
缓存失效缓存雪崩大量缓存key失效或者缓存实例宕机导致的大批量数据在缓存中无法查到,进而请求数据库,造成数据库压力过大的情况。应对这种情况,对于前者,在设置key的过期时间的时候就应该避免大量key过期时间相同,可以在过期时间上添加一个随机时间范围,避免短时间内大量数据被删除。对于后者,缓存实例可以采用集群部署,使用redis的主从同步方案,这样即使主库挂掉,也会迅速选择一个从库作为主库提供服务。同时缓存雪崩问题对缓存服务进行熔断、降级或限流,避免整个应用挂掉。缓存击穿少量访问量巨大的热点key失
2021-03-29 16:26:44
90
原创 redis的底层数据结构
redis的底层数据结构redis支持的数据类型有五种,字符串、列表、集合、哈希、有序集合。其中用于实现的底层数据结构有六种,简单动态字符串、整数数组、哈希表、双向链表、跳表、压缩列表。简单动态字符串简单动态字符串,底层使用char数组保存数据,但是会进行预分配,提前分配较大的空间,避免字符增加时每次都需要扩容,但是会占用额外空间。同时使用变量记录字符串已使用的长度,可以在O(1)时间复杂度内获取字符串的长度。整数数组整数数组作为集合的一种实现形式,它主要以有序、无重复的方式存储数字。底层存储的数
2021-03-26 17:02:53
69
原创 Mysql 内存使用
Mysql的内存使用Mysql虽然将所有的数据持久化在磁盘上,但是不可能每次查询、更新都直接对磁盘操作,那样效率也太低了。Mysql在实际sql语句执行过程中会充分利用内存进行加速读、写、排序、连接等。buffer poolbuffer pool是Innodb引擎的数据缓冲区。它主要有两个功能,一个是加速读,一个是加速写。当Innodb从磁盘上读取数据之后,会将数据存放在buffer pool中存储,之后下次查找直接从buffer pool中读取。当写入数据时,如果数据在buffer pool中已经存
2021-03-23 20:20:23
299
原创 Mysql 索引
Mysql 索引索引存在的目的就是为了加速查询。索引的实现方式Innodb引擎中索引只有B+树这一种实现方式。为什么选择B+树而不是数组、二叉树、B树,主要是考虑磁盘IO。Mysql作为一个数据持久化的数据库,所有的数据都是存储在磁盘上的,磁盘的访问性能在计算机的硬件中算是比较慢的,尤其是对于机械硬盘来说,随机访问速度更是慢的离谱,相比较而言,在内存中进行处理速度就快的多。因此想要加速数据查询,首先要做的就是尽可能减少磁盘IO,减少单次查询需要查询的磁盘次数。对于有序数组来说,数据需要进行二分查找,
2021-03-23 15:31:19
63
原创 MySql 幻读思考
什么是幻读幻读是指在事务执行期间,两次相同条件的读取,后面一次读取的结果中出现了前一次读取的结果中不存在的数据。出现这个问题的原因是因为其他事务执行之后新增了符合条件的数据,或将原有数据修改为符合条件。幻读的影响幻读带来的后果主要有两个:1. 结果不符合预期幻读会导致事务执行出现意料之外的结果,例如事务1查询主键为a的记录,如果不存在,则插入。假如事务1查询主键为a的记录不存在时,事务2插入了主键为a的记录并提交,那么事务1执行插入语句时就会报错。2. 主从数据同步时可能出现数据不一致问题假
2021-03-22 21:10:08
530
原创 Mysql 如何执行sql语句
Mysql如何执行一条sql语句mysql服务端主要分为两部分:server和存储引擎,server负责进行权限校验、sql语句解析、优化以及访问存储引擎;存储引擎主要负责数据的存取。当客户端向mysql server发送一条sql语句时主要经历以下步骤:客户端段访问mysql sever端的连接器,通过输入用户名密码来建立连接。mysql连接建立过程很复杂,实际使用过程中最好避免频繁的建立、断开连接。mysql查询缓存中是否命中了传入的sql语句。如果命中则直接返回查询缓存中的结果。但是由于数据更
2021-03-22 17:07:05
868
原创 Mock代理对象失效问题分析
Mockito 简介Mockito是一种常用的java单测框架,主要功能就是用来模拟接口的实现,对于测试环境无法执行的方法可以通过mock来执行我们定义好的逻辑。通常代码写法如下public class AimServiceTest { // 将mock对象注入到目标对象中 @Resource @InjectMocks private AimService aimService; // 生成mock对象 @Mock private NameS
2020-11-03 11:05:43
15376
3
原创 Mockito when函数实现方式
平时写单测时,对于一些有限制或因当前环境无法访问的接口时,需要用到Mock来为目标接口添加自定义的实现,使其表现出我们希望表现的逻辑,从而不影响单元测试的实现。Mockito是常用的一个类库,使用也比较简单。使用分为注解的方式和直接创建对象的方式。注解的方式// 被注入的目标对象@InjectMocksprivate AimService aimService;// 创建一个MockedService的一个mock对象@Mockprivate MockedService mockedSer
2020-11-03 11:04:38
13670
原创 cglib 动态代理原理详解
cglib 动态代理详解我们都知道jdk的动态代理内部调用切面无效的问题,而cglib则不会出现这种情况,这是为什么?cglib就一定不会出现内部调用切面无效的问题吗?cglib针对每一个类只创建了一个代理类吗?为什么cglib的效率要比jdk的动态代理低呢?首先我们看一下通常我们是如何使用cglib增强一个类的public class Main { static class Test{ public void test(){ System.out.pr
2020-11-03 11:01:42
1079
2
原创 java 类增强机制
前言对类的增强有多少种方式?我们首先想一下一个类的生命周期,从编码到程序运行结束:对类的增强,目的是为了在使用的时候能够在类原有的行为上进行增强,所以编码一直到使用中的几乎每个环节都可以对类进行增强,主要有以下几种方式:静态代理编译期织入:aspectJ加载时织入:1. 自定义classLoader;有容器能力;2. instrumention 动态attach运行时织入:动态代理1. 静态代理静态代理是最简单也是最容易理解的一种方式,只需要在编码的时候创建手动创建代理类调用即可,缺
2020-11-03 11:00:03
1870
1
原创 python函数参数
python 函数参数位置参数def f(x): return x * x默认参数(即有默认值的位置参数)def f(x, y=2): i, sum = [0, 1] while i < y: sum = sum * x i = i + 1 return sum默认参数必须放在位置参数前当有多个默认参数时,既可以按照顺序提供默认值,也可以不按照顺序提
2016-03-03 21:15:13
320
原创 JAVA泛型一
1. Java泛型的实现方式Java中实现泛型是从Java5开始的,由于历史遗留问题,即为了兼容低版本未用泛型实现的类库,Java采用了擦除的方式实现泛型,泛型代码在运行时会被擦除到他的边界范围。这样一来,在泛型代码内部,就无法获得有关泛型参数的类型信息。package suzumiya;import java.util.Arrays;public class Template { publi
2015-12-19 22:17:56
328
原创 排序算法之归并排序
归并排序也是效率较高的排序算法之一。其核心思想就是将两个已经排好序的序列合并成一个有序的序列。所以整个算法分成两部分,第一部分是分割,将一个长的无序的序列分解许多成只包含一个元素的序列。例如将序列:{12, 23, 8, 42, 15, 21}分解成{12},{23},{8},{42},{15},{21} 。第二部分是将分解好的序列进行归并。即先将序列归并成{12, 23},{8, 42},{15,
2015-07-18 21:44:19
331
原创 排序算法之快速排序
快速排序是典型的采用分治法进行排序的算法,即将一个长序列分割成短序列进行排序。快排的平均时间复杂度是O(n*log(n))。 快排的核心思想就是每次递归将一个基数放在它在整个序列排列的正确位置,即比它小的值都在他的左边,比它大的值都在他的右边。为了实现这一点,我们需要两个指针spoint和epoint,分别指向序列的起点和终点。spoint从起点向中间遍历,找到比基数大的数(升序排列如此),
2015-07-17 23:23:30
882
原创 c++11 对容器进行排序
本文主要总结如何利用容器对类类型的对象进行排序。 以容器set为例,存储类型为:class book{private: string name; double price;public: double getprice(){return price;} book(string n,double d):name(n),price(d){} voi
2015-06-15 21:36:11
636
原创 c++11 遍历容器的方法
以输出vector中的每个元素为例,四种遍历容器的方法。vector vec = {"c++","is","the","best","program","language"};普通for循环 for(auto beg = vec.begin(); beg!=vec.end();++beg){cout<<*beg<<" ";}范围for循环 for(auto temp : vec
2015-06-08 23:17:31
5049
3
原创 cordic算法原理及verilog实现
cordic算法原理及verilog实现算法原理 由此我们可以推出,当(x0,y0)与(x1,y1)的夹角为Θ时,满足如下关系: 由此可得,当(x1,y1)长度为1时,,当根据坐标旋转法旋转Θ角度后,坐标点变为(1,0)。因此,根据cordic算法求就是将初始线段旋转至(1,0)后,所得的(x,y)的值。 下面,我们将这些旋转步骤细化,看看每一步是如何工作的。 假设第n次旋
2015-06-07 20:13:56
18612
2
空空如也
c# 引用同一个解决方案下子项目的文件
2015-06-08
TA创建的收藏夹 TA关注的收藏夹
TA关注的人