导图社区 Joading
JAVA学习整理,会跟着自己学习的进度一直慢慢完善。JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
编辑于2021-04-29 17:44:04Joading
JVM
JVM模型
类加载器
class文件加载
运行时数据区
方法区(静态区)
存放: 1字符串常量 2static 3所有的class a) 被所有线程共享, 其内存放程序中永远唯一的元素,eg: static class
运行时常量池
子主题
子主题
堆
1 this 2 new出来的对象 3 数组 a) jvm只有一个堆区,并被所有线程共享
Java虚拟机栈
1 基础数据类型 byte short int long float double char boolean 2 方法的形式参数,方法调用完后从栈空间回收 3 引用对象的地址,引用完后,栈空间地址立即被回收,堆空间等待GC a) 栈内的数据线程之间独立 b) 具体细分为: b.1) 基本类型变量区 b.2) 执行环境上下文 b.3) 操作指令区
程序计数器
本地方法栈
执行引擎
本地接口
本地库
JVM调优
垃圾收集机制(GC)
执行 system.gc()的时候 空间不足的时候
垃圾收集器
并行(Parallel) 指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态; 如ParNew、Parallel Scavenge、Parallel Old; 并发(Concurrent) 指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行); 用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上; 如CMS、G1(也有并行) Minor GC 又称新生代GC,指发生在新生代的垃圾收集动作; 因为Java对象大多是朝生夕死,所以Minor GC非常频繁,一般回收速度也比较快; Full GC 又称Major GC或老年代GC,指发生在老年代的GC; 出现Full GC经常会伴随至少一次的Minor GC(不是绝对,Parallel Sacvenge收集器就可以选择设置Major GC策略); Full GC速度一般比Minor GC慢10倍以上;
G1
子主题
子主题
子主题
子主题
子主题
垃圾收集算法
引用计数
每次引用对象,就在对象存储存储一个计数,引用一次就+1,引用结束-1 当计数为0时,则清除该对象。
会存在循环引用的问题导致无法清理
解决该问题需要额外的开销
优化计数,多一个计数字段,当开始垃圾回收时,进行这个计数的引用扣除,把引用的新计数都-1,为0就说明可回收
占据额外的存储空间
可达性分析
定义了一个rootobj 每个对象引用的开始都是在ROOT中,如果某个对象追溯不到这个ROOT对象,那么则表示这个是垃圾
标记-清除
首先标记出所有需要回收的对象,标记完成后回收所有被标记的对象。不足主要体现在效率和空间,从效率的角度讲,标记和清除效率都不高;从空间的角度讲,标记清除后会产生大量不连续的内存碎片, 内存碎片太多可能会导致需要分配较大对象时,无法找到足够的连续内存而提前触发一次垃圾收集动作。 从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象,如果活着,就标记。只有全部标记完毕的时候,清理动作才开始。在清理的时候,没有标记的对象将会被释放,不会发生任何动作。但是剩下的堆空间是不连续的,垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。 优点:标记—清除算法中每个活着的对象的引用只需要找到一个即可,找到一个就可以判断它为活的。此外,更重要的是,这个算法并不移动对象的位置。 缺点:它的缺点就是效率比较低(递归与全堆对象遍历)。每个活着的对象都要在标记阶段遍历一遍;所有对象都要在清除阶段扫描一遍,因此算法复杂度较高。没有移动对象,导致可能出现很多碎片空间无法利用的情况。
导致产生很多内存碎片
标记-整理
过程与标记-清除算法一样,不过不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存。在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。这样就达到了标记-整理的目的。 优点:该算法不会像标记-清除算法那样产生大量的碎片空间。 缺点:如果存活的对象过多,整理阶段将会执行较多复制操作,导致算法效率降低。
每次都需要完整的整理内存,开销比较大
标记-复制
将可用内存分为两块,每次只用其中一块,当一块内存用完了,就将还存活的对象复制到另外一块上,然后再把已经使用过的内存空间一次性清理掉,循环下去。这样每次只需对整个半区进行内存回收,内存分配时也不需要考虑内存碎片等复杂情况,只需要移动指针,按照顺序分配即可。 优点:实现简单;不产生内存碎片 缺点:内存缩小为原来的一半,代价太高
内存开销需要之前的两倍
分代
把空间分为青年代和老年代 青年代中又分为伊甸区、存活区1和存活区2 老年代中则只有一个空间 新创建的小对象会放到伊甸区,而大对象则会放到老年代空间中 当伊甸区满了的时候,触发GC 当第一次GC时,把筛选出的非垃圾对象存放到存活区2中,清理存活区1+伊甸区的对象 当第二次GC时,把筛选出的非垃圾对象存放到存活区1中,清理存活区2+伊甸区的对象 在每次的GC中,存活下来的对象,都有个计数,并+1,代表年龄。如果年龄大于15,则说明这个对象是要经常使用的,把它放到老年代空间中,而当老年代空间堆满时,会进行(Full GC) 如果Full GC使用太频繁的话,无疑会对系统性能产生很大的影响。所以要合理设置年轻代与老年代的大小,尽量减少Full GC的操作
青年代
存活区S1
存活区S0
伊甸区
老年代
年龄大于15
大对象
数据库
关系型数据库
MYSQL
索引
存储引擎
Innodb
聚集索引
把具体数据跟索引存到一起,不用通过磁盘地址再进行查询 优点:效率更高 缺点:占用内存增加
MYIASM
非聚集索引
通过索引查询地址值,再从磁盘取数据 优点:减少了加载进内存时候的内存占用 缺点:多了一次磁盘IO,查询效率变低
索引算法
二叉树
按照大小排序,小在左 ,大在右。 优点:在一些情况下不需要全表扫描 缺点:当键为递增时也相当于全表,并未起到优化作用
红黑树
又叫平衡二叉树 当树高过高时,他会把树节点进行上移,进行平衡 优点:解决了二叉树的缺陷 缺点:树高还是太高,优化不足
BTree
B-Tree 他把红黑树的每个节点进行扩容,分配一个空间(16k)来存储索引和对应的数据存储文件的地址,在索引中间留给下一层节点空间。查询的时候分别加载到内存进行IO查找,通过索引找到地址值,再通过地址值去磁盘拿到相应的数据 优点:树高变低,只需要很短时间就可以查到数据 缺点:每个索引都附带了一个地址值,占用索引存储的空间,数据无排序,不支持范围查询
B+Tree
B+Tree 在B-Tree的基础上进行优化 优点:1.为聚集索引,能更快的直接拿到数据 2.在非叶子节点只存储索引,不存地址,节约空间,能放下更多的索引 3.进行了排序,叶子节点是双向链表的结构,能很快的进行范围查询 缺点:B+树最大的性能问题在于会产生大量的随机IO,主要存在以下两种情况: 1.主键不是有序递增的,导致每次插入数据产生大量的数据迁移和空间碎片; 2.即使主键是有序递增的,大量写请求的分布仍是随机的;
SQL优化
使用索引覆盖优化回表操作
使用索引覆盖进行优化
建立联合索引
最左匹配原则
索引下推
Index Condition Push 索引下推 是指在二级索引(也就是非聚集索引)下,尽可能的利用索引的优势 不使用的时候是先从第一个条件开始,先查出数据,再做筛选 开启索引下推后,当查询条件中包含索引,那么就会先在索引这层就进行筛选,然后在用地址值去拿完整的数据
前缀索引
当一个索引很长,或者是当索引的前面都是相同的,比如邮箱、身份证住址, 邮箱大都以www.开头,那么这几个字段就是没有必要的,会占用索引的内存,所以可以把这部分截去,然后生成索引。 或者前缀辨识度不高,像地址,都是某某省某某市,可以把数据进行翻转,然后创建索引 但是前缀索引这个操作是一定会走回表的,因为他不能确定你是否是正确的索引 对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能。 需要注意的是,优化器并不是要放弃使用这个索引。 这个时候大家可以用一些取巧的方法,比如 select * from tradelog where id + 1 = 10000 就走不上索引,select * from tradelog where id = 9999就可以。
事务
隔离级别
未提交读(脏读)
脏读-也是未提交读 最低最不安全的隔离级别。 如果同时两个事务操作同一条数据,一个读一个写;比如: A事务有一条select语句准备读一条数据,A开始后,select读之前有一个B事务开启,并修改了这条数据,那么在这个级别下,这个读的事务会读取到B已经修改后的数据。但是如果A查询到这条B修改完还没有提交事务的数据后,B事务回滚了,那么A就相当于读了一条不存在的数据
已提交读(不可重复读)
已提交读-不可重复读 在这个级别下,如脏读例子中的情况,就必须等B事务要提交后,A事务读取才能读到修改后的值。
可重复读
这个级别下就只能读到第一次读到的值,就算后面又有事务对他进行了修改,但还是只会查询出第一次读到的数据
串行化
串行化就是当事务A开启后,执行提交事务前,开启了事务B,这个情况下B事务会进入等待的状态,会等待事务A执行完成并提交后,才会开始事务B的执行。所以这个级别会导致效率很低。
MVCC
日志
Oracle
非关系型数据库
redis
技术支持的演变: redis支持的类型: string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
存储类型
string
list
hash
set
zset
持久化
AOF
记录每次的存储语句,恢复时数据完整,速度慢,文件大
rewrite
对AOF文件大的问题进行优化 因为每次都会记录存储的语句,而键相同的就只保留后一条 这就是rewrite
RDB
快照模式,存储当前某个时间的数据快照,因为是二进制压缩存储,所以占用空间很小,恢复时间短。但是数据有可能造成丢失
混合持久化
先使用快速的RDB模式进行恢复,然后使用AOF补全可能不完整的数据
主从复制
当系统宕机的时候,虽然数据可以恢复,但是这个过程也是需要时间的,我们得想个办法 办法就是多增加一个实例作为slave,salve可以分担读的功能,master就主要用来写入数据,可以提高性能 而当一个宕机时,就会把从切换为主,继续提供服务
哨兵机制
当然,当系统出问题的时候,到人工反应,切换slave,也是需要一定的反应时间,还是得想想办法 要是能够自动切换那不就好了 OK。没问题,哨兵机制就是这样诞生得 他会向每个服务发送心跳,如果在设置得时间内没有心跳回复,则判定服务宕机,则切换主从 但是还有问题,如果只是单纯的因为这个哨兵的连接跟redis master有问题,没有连接上,而产生了误判。 怎么解决呢,那就多搞几个哨兵,都进行心跳监控 如果一个哨兵发现了一个服务有问题了,那就发起投票。用公示算法投票选举出一个leader,一般大家都只给第一个发起的投票,投票超过设定的阈值,就选举成功,让这个哨兵负责切换主从
子主题
集群
分片
运用
MongoDB
Cassandra
HBase
neo4j
中间件
缓存中间件
一般使用非关系型数据库
消息中间件
rabbitMQ
Kafuka
rocketMQ
搜索中间件
ES
Luscen
JAVA基础
集合
list
ArrayList
List 实现于conllection arrayList 实现于List接口 arrayList的内部是使用一个数组进行存储数据 默认的长度是10 当超过容量的时候,就会进行扩容,扩容1.5倍 扩容时是使用copyof 直接把老的数组值copy到新的数组容器中 所以基于这个特性,每次存储内存之间都是相连的,可直接根据空间的索引下标直接取得值,所以他的查询速度是很快的; 但是当在中间插入值,或者删除值的时候,都要进行内存空间的重新排序,所以性能很低。
linkedArrayList
这个list是使用的双向链表结构 每次存储会附带上一个数据的地址值,和下一个的地址值 所以排列是有序的,因为是有指针的,所以当查询的时候就只能等指针遍历查询到自己所需要的数据,如果数量多,结果靠后,查询时间就会比较长。 但是因为是双向链表的结构,往中间插入值或者删除值的时候就很快,并不是真的删除,而是地址值指向的变化。
set
map
新特性
Java8
Optional
是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。 示例:String isocode = user.getAddress().getCountry().getIsocode().toUpperCase(); 如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查: if (user != null) { Address address = user.getAddress(); if (address != null) { Country country = address.getCountry(); if (country != null) { String isocode = country.getIsocode(); if (isocode != null) { isocode = isocode.toUpperCase(); } } } } Optional<User> opt = Optional.ofNullable(user); User user = null; User user2 = new User("anna@gmail.com", "1234"); // 如果第一个参数为null,那么就使用orElse作为默认值 User result = Optional.ofNullable(user).orElse(user2);
Optional.ofNullable()
可以允许对象为null
Optional.of()
会在null时抛出异常 NullPointerException
isPresent()
判断值是否存在 @Test public void optionalTest() { User user = null; // 创建一个User类的Optional容器 Optional<User> opt = Optional.ofNullable(user); // 如果存在才进入执行 opt.ifPresent(u -> { UserInfo adress = opt.get().getInfo(); System.out.println(adress); }); }
Lambda表达式
default关键字
静态默认方法
Stream
子主题
子主题
子主题
java框架
spring
Spring Cloud Alibaba
Spring Boot
Spring Batch
Spring Cloud
Eureka
服务注册中心。 当存在多个服务时,不可能每个服务写一个调用去相互之间调用 有了Eureka之后就可以
子主题
Spring Reactor
other
网络通信协议
HTTP
HTTPS
TCP
建立一个TCP需要以下部分组成: Socket:由 IP 地址和端口号组成 序列号:用来解决乱序问题等 窗口大小:用来做流量控制 如何确定一个TCP连接:(地址在IP中,端口在TCP中) 源地址 源端口 目标地址 目标端口
TCP头格式

序列号
序列号主要是用于客户端生成的随机序号,交给服务端进行验证
确认应答号
收到客户端的序列号和SYN=1 后,生成应答号,并把ack变为1返回给客户端
控制位
仅ACK为1,把从服务端收到的isn+1作为确认应答号返回,并可以携带数据返回
三次握手
四次挥手
UDP
因为不保证数据的完整性。而且不用建立连接,所以传输数据快。 一般用于: 视频、广播、音乐的传输
多线程
线程
线程的创建方式
实现Runable方法,重写run方法
继承Thread,复写run方法
实现Callable,重写call方法
线程池
线程池类型
线程池参数
volatile
volatile关键字 作用: 使线程之间数据的一致性 原理:volatile是作为底层的代码,实现他的不是java,是用C++语言实现的内存接近硬件层面的实现 当两个线程去处理同一个变量的时候,一个进行修改后,另一个是不会感知到修改的 因为多线程开启后,会把变量加载到主线程中,然后一个线程进行修改值的时候,会开辟一块单独的线程空间,里面会有一块工作空间,来存放主线程中变量的副本,当线程一修改的时候,是修改的副本,然后主线程检测到有修改,则会同步到主线程,而这个时候第二个线程读取变量的时候,还是之前的变量副本,并没有从主线程中获取最新修改的值,而加了volatile后,则会开启一个监听,一旦主线程的值变动,则会去同步主线程中的变量。 从而实现了数据的一致性 ================================================= 还有就是因为字节码文件加载到虚拟机的时候会有一个重排序的过程,有时候会导致某些代码执行顺序改变而造成运行结果的变动,而加了volatile后,就相当于给了这个代码一个标识,lock之类的,然后就不会对他优化重排序 它只能保证程序执行的结果时正确的,但是无法保证程序的操作顺序与代码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题
锁
偏向锁
当一多线程开启时,默认会在对象头中
轻量级锁(乐观锁,自旋锁)
CAS
重量级锁(悲观锁)
synchronized
修饰一个方法
修饰类
修饰代码块
设计模式
一种优化解决问题的思想
常用的设计模式
工厂模式
单例模式
观察者模式
原型模式
策略模式
模板模式
其他设计模式
架构
单体应用
分布式
微服务
集群
Spring Recator
响应式概述
诞生
背景
因为智能手机的发展,手机访问互联网的频率大幅提升。 手机的屏幕比较小,宽度通常在600像素以下;PC的屏幕宽度,一般都在1000像素以上(目前主流宽度是1366×768),有的还达到了2000像素。同样的内容,要在大小迥异的屏幕上,都呈现出满意的效果。
概念
可以为不同终端的用户提供更加舒适的界面和更好的用户体验
优点
页面布局
响应式设计可以向用户提供友好的Web界面,同样的布局,却可以在不同的设备上有不同排版,这就是响应式最大的优势
开发运维
响应式在开发维护和运营上,相对多个版本成本会降低很多。无须花大量的时间在网站的维护上
方便改动
方便改动,响应式设计是针对页面的,可以只对必要的页面进行改动,其他页面不受影响
缺点
不同设备
为了适配不同的设备,响应式设计需要大量专门为不同设备打造的css及js代码,这导致了文件增大,影响了页面加载速度
图像流的加载
图片、视频等资源一般是统一加载的,这就导致在低分辨率的机子上,实际加载了大于它的显示要求的图片或视频,导致不必要的流量浪费,影响加载速度
大页面
不适合一些大型的门户网或者电商网站,一个界面内容较多,对设计样式不好控制,代码过多会影响运行速度
Recator概述
优点
响应快
非阻塞。不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的
多线程环境
可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销
可扩展性
可以方便的通过增加Reactor实例个数来充分利用CPU资源
可复用性
reactor框架本身与具体事件处理逻辑无关,具有很高的复用性
缺点
门槛
Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试
大文件IO
Reactor模式在IO读写数据时还是在同一个线程中实现的, 即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写 ,会影响这个Reactor中其他Channel的相应时间,比如在大文件传输时, IO操作就会影响其他Client的相应时间,因而对这种操作, 使用传统的Thread-Per-Connection或许是一个更好的选择, 或则此时使用改进版的Reactor模式如Proactor模式 Thread-Per-Connection:对于每一个新的网络连接都会分配给一个线程,每隔线程都独立处理自己负责的输入和输出,高并发下会导致线程占用大,线程的反复创建、销毁、线程的切换也会导致性能的损失 tomcat为该模式
核心
Flux
表示的是包含 0 到 N 个元素的异步序列 在该序列中可以包含三种不同类型的消息通知: 正常的包含元素的消息、序列结束的消息和序列出错的消息。 当消息通知产生时,订阅者中对应的方法 onNext(), onComplete()和 onError()会被调用。
创建
创建一个Flux序列
Flux.generate()
Flux.generate(sink -> { sink.next("Hello"); sink.complete(); }).subscribe(System.out::println);
Flux.create()
Flux.create(sink -> { for (int i = 0; i < 10; i++) { sink.next(i); } sink.complete(); }).subscribe(System.out::println); 与generate创建的区别 create使用的是 FluxSink 对象,FluxSink 支持同步和异步的消息产生,并且可以在一次调用中产生多个元素
Flux.just()
将已有的数据交给Flux发送,发送完成之后会自定发送complete事件
Mono
表示的是包含 0 或者 1 个元素的异步序列。 该序列中同样可以包含与 Flux 相同的三种类型的消息通知。 Flux 和 Mono 之间可以进行转换。 对一个 Flux 序列进行计数操作,得到的结果是一个 Mono对象。 把两个以上 Mono 序列合并在一起,得到的是一个 Flux 对象
创建
创建一个Mono序列
Mono.fromSupplier()
Mono.fromSupplier(() -> "Hello").subscribe(System.out::println);
Mono.justOrEmpty()
Mono.justOrEmpty(Optional.of("Hello")).subscribe(System.out::println);
Mono.create()
Mono.create(sink -> sink.success("Hello")).subscribe(System.out::println);
Mono.just()
将已有的数据交给Mono发送,发送完成之后会自定发送complete事件
操作符
通过声明式的方式添加多种不同的操作符
range
表示一个范围,从几到几
buffer系列
Flux.range(1, 100).buffer(20).subscribe(System.out::println); 执行结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60] [61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80] [81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
buffer
把流收集到集合中,可指定最大收集数量
bufferUntil
bufferWhile
bufferTimeout
from系列操作符
from
fromArray
fromStream
fromIterable
错误处理
对于Flux或者Mono来说,所有的异常都是一个终止的操作, 即使你使用了异常处理,原生成序列也不会继续。 但是如果你对异常进行了处理,那么它会将oneError信号转换成为新的序列的开始, 并将替换掉之前上游产生的序列。
subscribe的时候指定onError
Flux flux2= Flux.just(1, 2, 0) .map(i -> "100 / " + i + " = " + (100 / i)); flux2.subscribe(System.out::println, error -> System.err.println("Error: " + error)); 转换为正常代码则为该形式: public void normalErrorHandle(){ try{ Arrays.asList(1,2,0).stream().map(i -> "100 / " + i + " = " + (100 / i)).forEach(System.out::println); }catch (Exception e){ System.err.println("Error: " + e); } }
Static Fallback Value
在遇到异常的时候会fallback到一个静态的默认值。比如onErrorReturn。
onErrorReturn
Flux flux= Flux.just(1, 2, 0) .map(i -> "100 / " + i + " = " + (100 / i)) .onErrorReturn("Divided by zero :("); flux.subscribe(System.out::println); onErrorReturn还支持一个Predicate参数,用来判断要falback的异常是否满足条件(是否为这个异常类) public final Flux<T> onErrorReturn(Predicate<? super Throwable> predicate, T fallbackValue) 例如: Flux flux= Flux.just(1, 2, 0) .map(i -> "100 / " + i + " = " + (100 / i)) .onErrorReturn(ArithmeticException.class,"Divided by zero :("); flux.subscribe(System.out::println); 如果是ArithmeticException的异常则打印Divided by zero :( 如果为NullPointerException异常则不打印,报错
Fallback Method
在捕获异常之后调用其他的方法
onErrorResume
public void useFallbackMethod(){ Flux flux= Flux.just(1, 2, 0) .map(i -> "100 / " + i + " = " + (100 / i)) .onErrorResume(e -> System.out::println); flux.subscribe(System.out::println); }
Dynamic Fallback Value
根据你抛出的异常进行判断,通过定位不同的Error从而fallback到不同的值
测试