通过javac执行java源文件,会生成一个 .class 的二进制字节码文件
JDK自带的反编译器: javap -help -查看帮助文档
javap -c java源文件名 ------ 就可以反汇编了
.java文件 ------------ > .class 文件 ------------ > JVM解析
Java源码首先被编译成字节码,再由不同平台的JVM解析,Java语言在不同平台上运行时不需要重新进行编译,Java虚拟机在执行字节码的时候,会把字节码装换成具体平台上的机器指令。
为什么JVM不直接将源码解析成机器码去执行?
准备工作:每次执行都需要各种检查 兼容性:也可以将别的语言解释成字节码
Java虚拟机:Java虚拟机是内存中的虚拟机,JVM的存储就是在内存中
JAVA反射机制是指在运行状态中,对于任意一个类,都能够知道这个类 的所有属性和方法,对于任意一个对象,都能够调用他的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
写一个放射的例子? 通过Class.forName(“类的全限定名”),拿到类的class对象,假如为clazz
通过clazz.newInstance()方法创建 一个该类的对象
通过clazz.getDeclareMethod()获取类的私有方法(既可以获取该类中的所有方法),但不能获取到父类的方法,或者是实现的接口中的方法,同时需要关闭安全检查getHello.setAccessible(true);
通过getMethod可以获取公有的方法,同时可以获取父类和实现接口中的方法
ClassLoader在Java中有卓非常重要的作用,它主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流,它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoafer负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接,初始化等操作。
ClassLoader源码分析:核心方法loadClass , parent也是一个ClassLoader
ClassLoader的种类:
避免多份同样字节码的加载,因为内存是宝贵的,没必要保存同样两份类的字节码
类的装载过程:
loadClass和forName的区别:
内存简介:
可寻址空间根据操作系统不同,范围也不同
地址空间的划分:
当前线程所执行的字节码行号指示器(逻辑)
改变计数器的值来选取下一条需要执行的字节码指令
和线程是一对一的关系,即线程私有
对Java方法计数,如果是Native方法,则计数器的值为Undefined 不会发生内存泄漏
Java方法执行的内存模型
包含多个栈帧
局部变量表:包含操作方法执行过程中的所有变量
操作数栈:入栈,出栈,复制,交换,产生消费变量
递归为什么会出现StackOverFlowError?
递归过深,栈帧数超出虚拟机深度
虚拟机栈过多会引发OutOfMemoryError
元空间(MetaSpace)与永久代(PerGen)的区别? 元空间使用的是本地内存,而永久代使用的是jvm内存
优势:字符串常量池存在永久代中,容易出现性能问题和内存溢出
类和方法的信息大小难以确定,给永久代的大小指定带来困难
永久代会为GC带来不必要的复杂性
Java堆(Heap) 对象实例的分配区域
GC管理的主要区域
JVM三大性能调优参数 -Xms -Xmx -Xss的含义
内存模型中堆和栈区别: 联系:引用对象,数组时,栈中定义变量保存在堆中的目标地址
管理方式:栈自动释放,堆需要GC
空间大小:栈比堆小
碎片相关:栈产生的碎片远小于堆
分配方式:栈支持静态分配和动态分配,而堆仅支持动态分配
效率:栈的效率比堆高
JDK1.6:当调用intern()方法时,如果字符串常量池先前已经创建好该字符串对象,则返回池中该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串对象的引用。
添加到JDK1.6之后:当调用intern()方法时,如果字符串常量池先前已经创建好该字符串对象,则返回池中该字符串的引用。否则,如果该字符串对象已经在Java堆中,则将堆中此对象的引用添加到字符串常量池中,并且返回该引用,如果堆中不存在,则在池中创建该字符串并返回其引用。
标记:从根集合进行扫描,对存活的对象进行标记 ---- 利用可达性算法找到垃圾对象
清除:对内存从头到尾进行线性遍历,回收不可达对象内存
缺点:由于该算法不需要堆栈的移动,会产生大量不连续的内存碎片
分为对象面和空闲面
对象在对象面上创建
存活的对象被从对象复制到空闲面
将对象面的所有对象内存清除
优点: 解决了碎片化问题
顺序分配内存
简单高效
适用于对象存活率低的场景
优点: 避免内存的不连续性
不用设置两块内存互换
适用于对象存活率高的场景
垃圾回收算法的组合拳
按照对象生命周期的不同划分区域以采用不同的垃圾回收算法
目的:提高JVM的回收效率
在jdk7之前,JAVA堆内存分为年轻代,老年代,永久代
在JDK8之后,永久 代就被去掉了
年轻代的存活率低,就使用的是复制算法,老年代的存活率高,就使用的是标记整理算法
GC的分类 Minor GC: Minor GC是发生在年轻代的垃圾回收动作,所采用的是复制算法,年轻代几乎是所有对象出生的地方,以及Java对象内存的申请和存放,都是在这里,新生代是GC收集垃圾对象的频繁区域。
Full GC:与老年代相关,由于对老年代的回收,一般会伴随着年轻代对象垃圾的收集,因此,第二种GC被称为Full GC.
年轻代:尽可能快速地收集掉那些生命周期短的对象
Eden区:两个Survivor区:
对象如晋升到老年代?
常用的调优参数:
老年代:存放生命周期较长的对象 标记清理算法或者标记整理算法
Full GC和 Major GC
Full GC比Minor GC要慢的多,一般会慢十倍以上,但执行频率低
触发Full GC的条件:
Stop-the-World JVM由于要执行GC而停止了应用程序的执行
任何一种GC算法中都会发生
多数GC优化就是通过减少Stop-the-World发生的时间来提高程序的性能,从而使系统具有高吞吐,低停顿的特点。
Safepoint 分析过程中对象引用关系不会发生变化的点
产生Safepoint的地方,方法调用,循环跳转,异常跳转等
安全点数量适中
Server:启动速度较慢------启动的是重量级JVM,运行速度更快 Client:启动速度较快------启动的是轻量级JVM,运行速度慢一些
Serial收集器(-XX:+UseSerialGC,复制算法)
ParNew收集器(-XX:+UseParNewGC,复制算法)
Parallel Scavenge收集器(-XX:+UseParallelGC,复制算法)
Serial Old收集器(-XX:UseSerialGC,标记整理算法)
Parallel Old收集器(-XX:UseParallelGC,标记整理算法) 多线程,吞吐量优先
CMS收集器(-XX:+UseConcMarkSweepGC,你标记清除算法)
最普遍的引用:Object obj = new Object();
在内存不足时,JVM宁可抛出OutOfMemoryError终止程序也不会回收 具有强引用的对象
通过将对象设置为null,来弱化引用,使其被回收
对象在有用但非必需的状态
只有当内存不足时,DC会回收该引用的对象的内存
可以用来实现高速缓存
非必需的对象,比软引用更弱一些
GC时会被回收,不管内存是否不足
被回收的概率也不大,因为GC线程优先级较低
适用于偶尔被使用且不影响垃圾收集的对象
不会决定对象的生命周期
任何时候都可能被垃圾回收器回收
跟踪对象被垃圾回收器回收的活动,起哨兵的作用
必须何引用队列ReferenceQueue联合使用
引用队列:ReferenceQueue
String 最基本的数据类型,二进制安全
Hash String元素组成的字典,适合于存储对象
List 列表,按照String元素插入的顺序的排序
Set String元素组成无序集合,通过哈希表实现,不允许重复
Zset 通过分数为集合中的成员进行从小到大的排序
使用Keys线上业务的影响
keys pattern :查找所有符合给定模式pattern的key
keys指令一次性返回所有匹配的key
键的数量过大会使服务卡顿,对内存的消耗和redis服务器都是隐患
SCAN cursor [MATCH pattern] [Count count]
基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程
以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历
不保证每次执行都返回某个给定数量的元素,支持模糊查询
一次返回的数量不可控,只能是大概count参数
第一条数据就是游标,第二条数据就是查找到的结果集,下一次迭代通过该游标进行继续迭代,通过该方式可能获取倒重复的数据,需要在Web程序去重
分布式锁需要解决的问题:
SETNX key value
:如果键不存在,则创建并赋值,时间复杂度为 O(1),返回值:设置成功,返回1;设置失败,返回0
如何解决SETNX长期有效的问题?
方式一:
EXPIRE key seconds
给key设置过期时间,当key过期时,就会被自动删除
缺点:原子性得不到满足,虽然SETNX
和EXPIRE
指令都是原子性的,但组合起来就不是了
方式二:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
SET操作成功完成时,返回OK,否则返回 nil
大量key同时过期的注意事项:
使用List作为队列,RPUSH生产消息,LPOP消费消息
缺点:不会等待队列有值才去消费
弥补:可以通过在应用层引入Sleep机制去调用LPOP重试
有没别的方法?
BLPOP key [key...] timeout
: 阻塞直到队列有消息或者超时
缺点:只提供 一个消费者消费
如何实现生产一次,并让多个消费者消费呢?
使用pub/sub 主题订阅模式
发送者pub发送消息,订阅者sub接收消息
订阅者可以订阅任意数量的频道
缺点:消息的发布是无状态的,无法保证可达
保存某个时间点的全量数据快照
SAVE:阻塞Redis的服务器进程,直到RDB文件被创建完毕
BGSAVE:Fork(派生)出一个子进程来创建RDB文件,不阻塞服务器进程
可以通过lastsave查看 上次保存RDB文件的时间
自动触发RDB持久化的方式
根据redis.conf配置里的SAVE m n 定时触发,用的是BGSAVE
主从复制时,主节点自动触发
执行Debug Reload
执行Shutdown且没有开启AOF持久化
系统调用fork(): 创建进程,实现了Copy-on-Write
如果有多个调用者同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针,指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用的副本给该调用者,而其他调用者所见到的最初的资源操持不变。
缺点:内存数据的全量同步,数据量大会由于I/O而严重影响性能。可能会因为Redis挂掉而丢失从当前至最近的一次快照期间的数据
保存写状态
记录下除了查询以外的所有变更数据库的指令
以append的形式追加保存到AOF文件中
日志重写解决AOF文件大小不断增大的问题,原理如下:
调用fork,创建一个子进程
子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件
主进程持续把新的变动同时写到内存和原来的AOF里
主进程获取子进程重写AOF完成信号,往新AOF同步增量变动
使用新的AOF文件替换掉旧的AOF文件
RDB和AOF文件共存情况下的恢复流程?
RDB和AOF的优缺点 RDB优点:全量数据快照,文件小,恢复快
RDB缺点:无法保存最近一次快照之后的数据
AOF优点:可读性高,适合保存增量数据,数据不易丢失
AOF缺点:文件体积大,恢复时间长
redis4.0之后提供了RDB-AOF混合持久方式:系统默认
BGSAVE做镜像全量持久化,AOF做增量持久化
使用Pipline的好处 Pipline和Liunx的管道类似
Redis基于请求/响应模型,单个请求处理需要一一应答
Pipline批量执行指令,节省多次I/O往返的时间
有顺序依赖的指令建议分批发送
slave 发送sync命令到master
master启动一个后台进程,将redis中的数据快照保存到文件中
master将保存的数据快照期间接收到的写命令缓存起来
master完成文件写操作后,将该文件发送给slave
使用新的AOF文件替换掉旧的AOF文件
master将这期间收集的增量命令发送给slave端
master接受到用户的操作指令。判断是否需要传播到slave
将操作记录追加到AOF文件
将操作记录传播到其他slave:1,对齐主从库 ;2,往响应缓存写入指令
将缓存中的数据发送给slave
Redis Sentinel 解决主从同步Master宕机后主从切换的问题
监控:检查主从服务器是否正常运行
提醒:通过API向管理员或其他应用程序发送故障通知
自动故障转移:主从切换
流言协议Gossip:在杂乱无章中寻求一致
每个节点都随机地与对方通信,最终所有节点的状态达成一致
种子节点定期随机向其他节点发送节点列表以及需要传播的消息
不保证信息一定会传递给所有节点,但是最终会趋于一致
如何从海量数据里快速找到所需?
分片:按照某种规则区划分数据,分散存储在多个节点上,不同的key放在不同的redis节点上
获取key的hash值,然后根据节点数取模,常规的按照哈希划分无法实现节点的动态增减
一致性哈希算法:对2的32次方取模将哈希值空间组织成虚拟的圆环,将数据key使用相同的函数hash计算出哈希值,这样就可以确定每台服务器在哈希环上的具体位置
在Liunx中如何让查找指定文件?
检索文件常用命令? 管道操作符 |
可将操作指令连接起来,前一个 指令的输出作为后一个指令的输入
注意:只处理前一个命令正确的输出,不处理错误的输出
如何对文件内容做统计? awk --------- 适合于对表格化的数据进行处理
语法:awk [options] 'cmd' file
一次读取一行文本,按输入的分隔符进行切片,切成多个组成部分,将切片直接保存在内建的变量中,1,2…(
定义一个数组名字。通过下标来保存引擎的名字,该数组的值就是对应引擎的值,一旦相同的引擎出现,则对应的值就在原来的基础上进行累加,END就是扫描结束后,要执行的操作,只要于操作相关 ,就要使用花括号,表里数组中的值,并将器其值打印出来
对内容逐行进行统计操作,并列出对应的统计结果,默认的分隔符是空格
如何批量替换文件中的内容?
sed
语法:sed [option] 'sed command' filename
全名:stream editor 流编辑器
适用于对文本的行内容进行处理
筛选出Str打头的行,并进行替换目标文件