java基础知识

java 内存区域、JMM、JAVA线程模型、硬件内存模型

  • java内存区域分为共享区域(堆、方法区常量池)、私有内存区域(程序计数器、虚拟机栈、本地方法栈)
    • java 内存堆
    • 方法区常量池,方法区主要存储虚拟机加载类信息、常量、static变量、动态编译器编译的信息
    • 程序技术器,记录方法循环、控制流程等计数信息
    • 虚拟机栈,每个方法都一个栈帧包含本地变量、操作数表、动态链接方法、返回值、返回地址
    • 本地方法栈,是用来记录本地方法
  • JMM(java内存模型),JMM是一组内存分配规范,JVM为每一个线程都分配一块工作内存,JVM规定类中的成员变量存储在堆中,方法的本地变量存储在栈中,线程方法运行前将所有变量从主内存拷贝到工作内存,完成操作后,修改主内存中的变量
  • JAVA线程模型,java中线程与内核中lwp(Light weight process)一一映射
graph TB
    A[任务]-->D[Executors]
    B[任务]-->D
    C[任务]-->D
    D-->E[Thread LWP]
    D-->F[Thread LWP]
    D-->G[Thread LWP]
    E-->H[内核线程]
    F-->I[内核线程]
    G-->J[内核线程]
    H-->K[OS kernel线程调度器]
    I-->K
    J-->K
    K-->L[CPU]
    K-->M[CPU]
    K-->N[CPU]
  • 硬件内存模型,分为cpu、寄存器、缓存、内存

java的对象创建及内存分配

对象创建

  1. jvm检查常量池里面是否有类加载标示,如果没有先加载类
  2. 内存分配方式是指针碰撞和空闲列表分配
  3. 指针碰撞主要是使用规则的内存块,根据指针和偏移长度分配,但是这种方式受垃圾回收器影响
  4. 空闲列表分配是jvm会存储空闲内存区域列表,根据空闲内存列表进行内存分配
  5. 对象的使用是栈来引用堆中的内存对象

内存分配

  • java堆内存分为新生代和老年代
  • 新生代包含EDEN和两个survivor区,内存分配默认是8:1:1
  • 当eden区分配内存不足时,会进行minorGc,新生代会将存活的对象根据复制算法复制到另一个不使用的survivor区。
  • 当survivor区内存不足时,会采用分配担保策略将对象移植到老年代
  • fullGc是发生在老年代gc会触发stop the world停顿,所以需要避免发生full gc
  • 对象的内存分配一般在新生代
  • 对象的内存分配当EDEN区没有足够的连续内存会先触发一次Gc,然后在老年代中进行分配,因此不能进行大对象分配

volatile关键字

  • 可见性
  • 指令重排

多线程的重要特性

  • 原子性
  • 可见性
  • 顺序性
  • happens-before
    • 传递性,
    • 线程内执行顺序一致性
    • 一个线程的start方法是所有的开始
    • 一个锁的unlock必然在后续同一个锁的lock之前
    • volatile,一个线程内变量读取必须从主内存读取,对变量的写操作会同步刷新到主内存
    • 对象的构造方法必然早于finalize方法
    • 线程的所有操作都早于线程的终止
    • 对线程的interrupt方法调用先行发生于被中断线程检测到中断事件的发生

引用(软引用、弱引用、虚引用)

  • 软引用softreference,主要用于内存敏感的高速缓存,当GC内存不足的时候才会回收
    • 回收时首先将referent设置为null,不再引用堆中的对象
    • 将堆中对象设置为finalizable
    • 调用finalize方法将对象释放,引用会放入RefrenceQueue中
  • 弱引用weakreference,GC会直接回收,过程类似软引用,使用场景就是使用但是不用关心回收的场景
  • 虚引用phantomreference,虚引用直接把对象放入到referent中,只是把get返回结果置为null,然后把heap中的对象设置为finalizable,GC把phantomreference放到ReferenceQueue中,然后释放虚对象。使用场景是资源释放就可以把对象设置为虚引用。

synchronized关键字解析

使用场景

  • 静态成员变量,将会锁当前class所有对象
  • 实例方法,将会锁定当前实例方法
  • 代码块,将会锁定指定实例对象或指定class

锁定原理

  • 对象锁,对象分为对象头、对象实体、填充信息,锁的信息放在对象头markword里面,markword中锁标示指向对象监视器monitor;编译器在执行编译的时候,对synchronized对象前后增加monitorenter、monitorexit方法,当方法异常的时候也会执行monitorexit方法保证锁的释放;对象监视器为结构为waitset、entrylist、锁定标示,多线程获取锁进行entrylist,当前线程释放锁后修改锁定标示,然后从entrylist获取下一个线程信息,变更锁定标示为锁定状态,当线程进入wait状态会将线程写入waitset;
  • 方法锁,主要依靠常量池方法的ACC_SYNCHRONIZED标示,标定是否是锁定状态

锁优化(jdk1.6)

  • 偏向锁,对象锁markword锁标示默认是指向偏向锁的,当然一个线程多次获取同一个锁时,使用偏向锁,当多线程获取锁定对象时,膨胀为轻量锁
  • 轻量锁,对象锁markword锁标示执行轻量锁,当多个线程交替获取锁时为轻量锁经验认为90%的情况是两个线程交替执行较多,当多个线程互相竞争获取锁时,锁膨胀为自旋锁
  • 自旋锁,自旋锁,通过线程循环空转,尝试获取锁,一般次数空转次数为50-100次,仍然无法获取锁,则将调用系统将线程挂起等待,膨胀为重量锁
  • 锁消除,当一结构在使用场景无多线程竞争,则删除锁

其他

  • synchronized是通过编译器,修改该字节码进行锁定和释放的,因此是隐式锁;与之相对应的是ReentarentLock是通过AQS实现的,需要明确的进行锁的增加和释放
  • synchronized与ReentrantLock都是重入锁,允许通过当前对象锁定线程多次对当前对象进行加锁

ReentrantLock实现原理

  • ReentrantLock是基于AQS实现的,是重入锁
  • ReentrantLock可以初始化为公平锁和非公平锁,默认为非公平锁,一般情况下非公平锁效率更高
  • FairSync(公平锁)当获取锁的时候,首先会检查等待获取队列里面是否有线程,有线程则挂起线程加入队列不获取锁
  • NonFairSync(非公平锁)当获取锁的时候不进行队列检查,直接获取

多线程常见问题

上下文切换

CPU给线程分配时间片,然后进行上下文切换

解决方案

  • 无锁设计分段
  • 采用CAS方法
  • 合理创建线程

死锁

两个线程分别等待对方释放锁
解决方案

  • 尽量一个线程只获取一个锁
  • 一个线程只占用一个资源
  • 尝试使用定时锁,最终锁会释放

资源限制

CAS原理(常用于无锁化设计)

  • 对比原值是否相等,确定相等,然后进行更新操作
  • Atomic调用unsafe类进行compareAndSet,unsafe调用操作系统的compare and exchange指令,多核时使用锁总线和锁缓存
  • CAS容易遇到ABA问题,即其他线程已经将数据进行修改了B,然后又修改回了A,此时CAS命令无法处理,可以使用时间戳标示如AtomicStampedReference

JAVA并发包(java.util.concurrent)

  • Executors:ThreadPoolExecutor、ScheduledThreadPoolExecutor、Future、FutureTask、ForkJoinPool、ForkJoinTask
  • Queue:LinkedBlockingQueue、ArrayBlockingQueue、SynchronizeQueue、PriorityBlockingQueue、DelayQueue
    • Queue的默认方法为add(添加成功返回true失败抛出异常)、offer(添加成功返回true失败返回false)、put(添加队列满则阻塞)、remove(删除对象成功返回true失败返回false)、poll(取出队头数据,失败返回null)、take(取出队头数据队列空阻塞)、element(获取但不移除队列的头元素,没有元素抛出异常)、peek(获取但不移除队列的头元素,没有元素返回null)
    • 结构区分:ArrayBlockingQueue底层基于数组是有界队列,LinkedBlockingQueue底层基于队列可以是有界也可以是无界
    • 性能区分:ArrayBlockingQueue底层put、take使用一个ReentrantLock,LinkedBlockingQueue底层put、take分别使用两个ReentrantLock因此吞吐量大于ArrayBlockingQueue
    • 空间区分:ArrayBlockingQueue基于数组因此队列的添加和删除不产生额外的数据,linkedBlockingQueue基于链表添加的时候会额外产生node对象,大批量增加的时候会产生大量数据对GC有影响
    • SynchronizeQueue 是一对一阻塞队列
  • Timing:TimeUnit
  • Synchronizer:Semaphore、CountDownLatch、CyclicBarrier、Phaser、Exchanger
    • Semaphore可以设置允许的多并发量
  • Concurrent Collection:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList、CopyOnWriteArraySet
  • Memory Consistency Properties:
  • Locks(java.util.concurrent.locks):ReentrantLock、ReentrantReadWriteLock、StampedLock
  • Atomic(java.util.concurrent.atomic):AtomicBoolean、AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpdater、AtomicLong、AtomicLongArray、AtomicLongFieldUpdater、AtomicMarkableReference、AtomicReference、AtomicReferenceArray、AtomicReferenceFieldUpdater、AtomicStampedReference、DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、

java中finally详解

  • finally当在return不在try finally块中的时候finally不执行;当在try finally中执行system.exit的时候finally不执行
  • finally块中和try块都有return语句,则finally中return先于try块中return执行;try中有return语句,finally中无return语句时,finally会在最后执行。

java中异常的层级结构

graph BT
    A[IOException]-->B[Exception]
    G[Error]-->F[Throwable]
    B-->F[Throwable]
    C[ClassNotFoundException]-->B
    D[RuntimeException]-->B
    E[CloneNotSupportException]-->B
    H[FileNotFoundException]-->A
    I[NullpointException]-->D
    J[ClassCastException]-->D
    k[EOFException]-->A

JVM定义内存区域划分

  • 虚拟机是抽象化的计算机,运行在实际的计算机中,有完整的硬件、堆栈、指令系统。jvm通过虚拟机与操作系统交互,虚拟机只运行标准字节码,实现了跨平台不做修改的运行。
  • JVM是一个普通的进程
  • JVM包含类加载子系统、执行引擎子系统、垃圾收集子系统
  • JVM类内存区域内存堆、虚拟机栈、方法区、常量池、本地方法区、程序计数器

JVM参数设置

  • 推荐设置堆内存为full gc后老年代内存的3-4倍,-xms -xmx
  • 推荐设置年轻代内存为full gc后老年代的1-1.5倍,-xmn
  • 推荐设置老年代内存为full gc后老年代的2-3倍
  • YGC设置大可以避免full gc,增大吞吐或者响应速度
  • 增加survivor空间比例可以有效减少promotion failed
  • 设置CMSFullGCBeforeCompaction=0可以开启对老年代的内存空间的整理
  • 使用hashmap作为缓存的时候,缓存不应该无限长建议使用LRUMap

JAVA基础分析工具

  • jps -m -l 查找java进程以及main方法,可以用ps -ef | grep xxx代替
  • jstack pid 根据进程的堆栈信息,top -Hp pid 查找最耗性能的线程ID,然后根据线程ID(转为16进制)找到线程堆栈
  • jmap 查看堆内存使用情况,也可以dump出来通过MAT查询
  • jstat 查看jvm运行状况

classloader加载

  • 类加载过程,加载(load)->验证(verify)->准备(Prepared)->解析(resolve)->初始化(initialize)
  • 类加载器依次是Bootstrap加载器->扩展加载器(extend)->应用加载器(System)
  • 类加载使用的双亲委托的模式
  • 双亲委托的破坏者是线程上下文类加载器,可以逆向加载SPI核心

对象加载与反射

  • 对象必须初始化的条件:
    • new 或者读取设置一个类的静态字段(不包含编译常量)以及调用静态方法的时候,必须触发类的初始化过程.
    • 使用反射包进行反射调用的时候,如果没有初始化必须进行初始化
    • 初始化一个类的时候父类没有被初始化,先初始化父类
    • 虚拟机启动时候的main方法类
    • jdk1.7动态语言支持
  • 反射是动态运行获取对象,包含Field\method方法
  • 通过反射可以动态创建数组

enum类型

  • 枚举的基础是final class extends Enum
  • 反射无法动态创建枚举类
  • 枚举扩展类包括EnumMap,EnumSet(位向量实现)

redis vs memcached

  • redis支持多种的内存数据结构和丰富的内存操作,可以使大部分操作都像处理get/set一样高效
  • 内存使用效率对比,普通的结构memcache更加高效,使用redis的hash结构内存的使用效率更加高效。
  • 性能对比,redis只能使用单核而memcached可以使用多核。在小于100k的数据存储上,redis单核效率更高,而大于100K的数据结构memcache效率更高

redis数据结构详解

string

默认为是字符串sdc.h结构{int len,int free, char[] def},当使用incr decr时为数值类型

hash

redis hash当长度小于512且每个value均小于64kb时,hash结构为ziplist,ziplist可以使用紧凑的结构实现多个元素的连续存储,因此ziplist可以有更好的提高内存使用效率;当长度大于512时,hash结构为hashtable作为内部的实现方案。redis hash结构的resize使用了物理均摊策略,触发resize后会生成isRehasing标志,期间每一个hget和hset均进行相应的数据entry挪动,并且后台也有线程进行分批次迁移。迁移的过程中,get 先从old table读取,然后从 new table读取,让迁移完成后进行hash执行newtable,然后将isRehashing设置为true。

list

list内部结构默认是双向链表,支持反向查找和遍历,同时记录了链表内记录了链表的长度

set

set是一个value为null的hashmap,通过key的hash进行排重

sorted set

sorted set内部使用hashmap和skiplist进行数据存储,

hyperLogLog、Geo、PubSub、BloomFilter(Bitmap)、RedisSearch、Redis-ML

redis常问知识点

  • 分布式锁,使用setnx+expire,setnx可以批量处理
  • redis搜索,当数量巨大时可以使用不影响性能的scan
  • redis异步队列,使用list做异步队列,消费者轮询lpop,也可以使用blpop不轮询阻塞读;如果要一个生产者多个消费者可以使用pub/sub,但是pubsub不会在消费者下线后存储消息,生产的消息丢失,因此要使用专业的mq;可以使用redis sortedset做延时队列,使用时间戳作为score,消费者使用zrangebyscore获取之前的数据处理
  • 如果有大量的key设置同一时间过期,可能会引起redis的停顿,可以在时间戳上加上随机值,让redis过期更加均匀一些
  • redis持久化通过rdb和aof进行,rdb是定时全量备份恢复时间短,aof按照一定的策略记录日志,但是恢复时间较长,redis会定期做aof重写;redis4.0使用混合模式将进行数据备份可以保证在数据较少丢失的情况下,快速恢复;
  • pipeline 将多次io压缩成一次处理,对于前后没有关系的命令可以使用这个方法可以增加吞吐量
  • redis主从同步机制,第一次主节点做一次bgsave,并同时将后续操作记录写入内存buffer,待到rdb文件全量同步到复制节点,复制节点将rdb文件全量加载到内存,加载完成后通知主节点将数据同步期间操作,同步到复制节点完成同步操作
  • redis集群;redis sentinal强调高可用,master宕机后自动将slave提升到master继续操作;redis cluster强调扩展性,在单个redis内存不足时,使用redis cluster进行扩展分片存储,最多可以扩展到16384个簇。
  • redis的最大内存策略失效策略,默认noeviction(不置换直接报错)、allkeys-lru 优先删除最近最不常使用的数据、volatile-lru 只从设置了失效时间的key里面按照lru删除、volatile-ttl只删除离失效时间最近的key、all-random 从所有key里面随机删除、volatile-random从expire set里面随机删除

redis keys * scan

  • keys * 的缺点
    • 没有offset、limit一次性输出全部的数据
    • keys 是遍历key数据过大的时候会产生卡顿
    • keys 匹配模式
  • scan 的特点
    • 游标不阻塞线程
    • limit 限制返回的数据量
    • 提供匹配模式
    • 服务器端不保存游标数据,游标的状态是返回给客户端的数据
    • 返回的结果可能有重复
    • 遍历的过程中,有对数据的修改,新修改的数据是否能够轮询到不确定
    • 单次返回的结果为空并不代表遍历结束,要看到返回的游标值是否为空

redis常见问题总结

  • server端
    • 内存规划,bgsave造成的内存溢出。通过优化内存最大值,更改linux参数设置
    • 连接数超出。增大连接数限制和客户端优化
    • AOF性能问题。关闭AOF或者使用async everysec
  • client端
    • 使用Pipline和批量命令。
      • 线上发现任务执行缓慢问题
      • ps -ef|grep java 找到进程id,或者jps -v
      • jstack 导出日志,发现大量redis连接
      • 代码中循环使用redis实时连接
    • 错误使用数据类型和操作命令
      • 使用string,存储数据,客户端查找;可以使用key:主键查找或者hash存储
      • set客户端查找,判断是否包含存在直接客户端取出全部进行内存查找
      • incr或hincr数据增长,客户端操作
    • 不设置失效时间。导致内存暴涨

netty线程模型

  • reactor反应模型
  • channelFuture是java.util.concurrent.Future的接口扩展实现
graph TB
    A[serverSocketChannel]-->|多Channel|B[NioEventLoop,Selector]
    A-->|多channel|C[NioEventLoop,Selector]
    B-->|Channel,ByteBuf|D[channelHandlerPipline]
    C-->|Channel,ByteBuf|E[channelHandlerPipline]

netty粘包和拆包

TCP包按照流的方式进行传输,根据数据大小、缓存区不同、TCP/IP报文最大大小、以太网payload帧大小不一致导致的TCP包的拆分和重组。

  • 按照固定长度进行拆包和粘包
  • 按照固定分隔符进行拆包和粘包
  • 按照协议头和协议体进行拆包和粘包
  • netty所有协议解析均继承自ByteToMessageDecoder、ChannelHandlerAdapter,目前netty常用的解析器为FixedLengthFrameDecoder、LineBasedFrameDecoder、DelimiterBasedFrameDecoder、HttpRequestDecoder、HttpResponseDecoder

netty zero-copy

netty的zero-copy是通过ByteBuf的操作实现数据的读取,避免了重新定义newarray

http 协议相关、get、post、tcp/ip

  • http超文本传输协议
  • http请求分为请求头和包体
  • HTTP1.1默认为keep-alive,可以采用多个连接pipline方式通讯,严格限制请求和响应的顺序;可以采用ascII和二进制混合传输方式
  • HTTP2建立到服务器的单链接,使用mulit方式通讯,请求和响应没有严格的顺序,提高了传输效率;http2采用二进制传输方式,且可以服务端推送消息
  • get方式请求参数直接放在URI,通常用于查询操作,由于可以在浏览器端明文展示用户信息,且部分浏览器会对请求连接进行缓存,相对不够安全,在部分浏览器和服务器有请求长度的限制
  • post方式请求参数放在http请求包里面,常用于数据传输、增加更新等操作,由于在浏览器端无法直接看到请求参数,且浏览器对于post参数默认不进行缓存,相对于get较安全,传输参数长度理论上无限制,只受限于服务端设置的大小
  • tcp分为三次握手和四次挥手协议。

    超时重发:发送者向接受者发送包、当达到超时时间没有回复就会重发;如果一次性发送三个包,如果收到了最后一个包的确认就默认三个包全部发送成功。

    滑动窗口:通过多个包边发边接受用来提高效率。这种就是滑动窗口协议

三次握手
sequenceDiagram
    client->>server: sync
    Note left of client: SYNC_SEND
    server-->>client: sync+ack
    Note right of server: SYNC_RCVD
    client->>server: ack
    Note right of server: ESTABLISED
    Note left of client: ESTABLISED
    
四次挥手
sequenceDiagram
    client->>server: fin
    Note left of client: FIN_WAIT-1
    server-->>client: ack
    Note right of server: CLOSE_WAIT
    Note left of client: FIN_WAIT-2
    server->>client: fin
    client-->>server: ack
    Note right of server: CLOSE
    Note left of client: TIME_WAIT

ACID、CAP、BASE

  • ACID事务的特性,原子性Atom、一致性Consistency、隔离性isolation(read uncommited未提交读、readcommited已提交读、repeate read可重复读、serialize串行化)、持久性durability
  • CAP分布式系统的一致性consistency、有效性available、分区容错性partitionTolerance,分布式系统一般只能满足其中两个
  • BASE是可用性Base available,软状态Soft state,最终一致性Eventual consistency

SQL优化、分库分表

  • sql优化
    • 负向查询不走索引
    • 前导模糊不走索引
    • 字段计算无法使用索引
    • 符合索引最左前原则
    • 只有一条语句直接limit 1
    • join的字段类型要相同,才可以命中索引
    • 字段的默认不要为null
    • 数据区分不明显不建议创建索引
  • 分库分表
    • 水平分表
      • 按ID取模、时间或固定行数进行分表
      • 分表后查询语句变得复杂,可以按照多个语句进行查询然后在进行合并
    • 垂直分表
      • 当数据字段过多时将数据按照使用频次拆分为主表和扩展表
      • 仍然建议分开查询
    • 两段提交
      • 保证最终一致性,A调用B,两个执行成功才算成功
      • 方案为失败时B通过MQ将消息通知A,A再来回滚。前提是A的回滚操作是幂等的

spring boot启动流程(SpringApplication.run(args))

  1. 初始化SpringApplication实例,然后调用run方法;初始化实例之前会进行几步操作
    • 根据classpath的特征推断ApplictionContext类型是Web类型还是Standalone类型
    • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer
    • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener
    • 推断并设置main方法的定义类
  2. SpringApplication初始化完成并完成设置后,就开始执行run方法;run方法首先遍历执行通过SpringFactoriesLoader查找并加载的SpringApplicationRunListener,并调用他们的start()方法
  3. 创建并配置当前SpringBoot将要使用的Environment(包括配置要使用的PropertySource以及Profile)
  4. 遍历调用所有SpringApplicationRunListener的environmentPrepared方法
  5. 根据设置showBanner
  6. 根据用户是否明确设置了applicationContextClass类型以及初始化推断结果,决定改为当前springBoot应用创建什么样类型的ApplicationContext并创建完成,判断是否添加shutdownHook,判断是否使用自定义的BeanNameGenerator,判断是否使用自定义的ResourceLoader,将准备好的Environment设置给ApplicationContext使用
  7. ApplicationContext创建好之后,会再次借助SpringFactoriesLoader,查找并加载所有可用的ApplicationContextInitializer,并调用其initialize方法对ApplicationContext进行进一步处理
  8. 遍历调用所有的SpringApplicationRunListener的contextPrepared方法
  9. 将@EnableAutoConfiguration获取的所有配置以及其他形式的IOC容器配置加载到ApplicationContext
  10. 遍历调用所有的SpringApplicationRunListener的contextLoaded方法
  11. 调用ApplicationContext的refresh方法,完成Ioc容器的最后一步工序
  12. 查找ApplicationContext中是否注册有CommandLineRuner,如果有遍历执行他们
  13. 正常情况下遍历执行SpringApplicationRunListener方法的finished方法,出现异常也会调用,只是会将异常信息一并传入处理
graph TB
    A((开始))-->B[加载ApplicationContextInitializer和ApplicationListener]
    B-->|SpringApplicationRunListener.start|C[load Environment]
    C-->|SpringApplicationRunListener.environmentPrepared|D[init ApplicationContext,set Environment,load Property]
    D-->|SpringApplicationRunListener.contextPrepared|E[ApplicationContext Load Other Ioc]
    E-->|SpringApplicationRunListener.contextLoaded|F[refresh ApplicationContext]
    F-->|Run CommandLineRuner,SpringApplicationRunListener.finished|G(结束)

Spring IOC与AOP,BEAN的生命周期,spring mvc处理流程

  • IOC指的是IOC容器,IOC通过DI(依赖注入)和DL(依赖查找)进行工作
  • IOC的作用是收集和注册Bean、分析和组装Bean
  • AOP的底层原理是依赖于动态代理,基于接口实现动态字节码,生成新的类;
    AOP也可以通过CGLib进行,是基于子类的,通过ASM生成动态字节码进行类的加载
  • Spring Bean的生命周期
graph TB
    A[Bean实例化]-->B[属性设置]
    B-->C1[BeanNameAware.setBeanName]
    C1-->C2[BeanFactoryAware.setBeanFactory]
    C2-->C3[ApplicationContextAware.setAppliactionContext]
    C3-->D[BeanPostProcess.setBeanPostBeforeInitialization]
    O[continue]-->E[InitializingBean.afterPropertiesSet]
    E-->F[init-method定制的初始化防范]
    F-->H[BeanPostProcess.setBeanPostAfterInitialization]
    H-->|Bean准备就绪|J[DisposeBean.destory]
    J-->K[destory定制的销毁防范]
  • Spring Mvc的处理流程
sequenceDiagram
    Client->>DispatchServlet: request请求
    DispatchServlet->>handlerMapping: 请求Handler
    handlerMapping-->>DispatchServlet: 返回执行链
    DispatchServlet->>handlerAdapter: 请求适配器执行handler
    handlerAdapter->>handler: 执行handler
    handler-->>handlerAdapter: 返回结果
    handlerAdapter-->>DispatchServlet: 返回ModelAndView
    DispatchServlet->>ViewResolver:请求视图解析器
    ViewResolver-->>DispatchServlet: 返回view
    DispatchServlet->>view视图jspfreemarker:视图渲染
    DispatchServlet->>Client: response
  • springMvc vs Struts
    • Struts2是基于类拦截,spring mvc是基于方法拦截的
    • Struts2拦截器是自己实现的,spring mvc是基于AOP的
    • Struts2入口是filter,spring mvc是基于sevlet的
    • Springmvc集成ajax,struts2需要第三方支持
    • Spring mvc跟spring 更能无缝配合

缓存淘汰策略LRU\LFU\FIFO

  • LRU最近最少使用,通常实现方式为hashmap+链表,使用通过hashmap查找,然后将当前值移至链表头部,删除的时候就直接删除链表的tail。
  • LFU最近最小频率使用,常用实现方式为hashmap+数组,hashmap查找,数组记录数据和使用次数
  • FIFO先进先出。使用hashmap+链表方式,hashmap查找,链表记录数据,删除tail数据。

zookeeper使用场景

  • 配置中心
  • 负载均衡(dubbo)
  • 命名服务(naming service)
  • 分布式通知协调
  • 分布式锁
  • 分布式队列
  • 集群管理与Master选举

消息队列常见的问题

  • 消费端重复消费,建立去重表
  • 消费端消息丢失,消费完成才能确认,kafka中消费完成后返回offset
  • 生产者重复发送,消费端根据去重表删除
  • 生产者消息丢失
    • 消息队列满了,进行阻塞重复发送
    • 建立回调,消费完成后会进行回调

dubbo服务化分析

  • 高性能RPC
    • 自定义协议
    • 基于netty的长连接
    • 堆缓冲区
  • 服务发现与治理
    • 服务发现zookeeper、redis
    • 路由、集群容错、限流(并发限流基于信号量限流)
    • 负载均衡(权重分配、最少活跃优先、一致性hash、roundrobin)
    • 服务动态配置
  • 服务隔离
    • 自定义熔断机制
    • hystrix机制
  • 启动与停机
    • 延迟暴露
    • 服务预热
    • 优雅停机

日志分析理解

  • 日志使用频率是低频的,要求准实时的,因此数据直接放入elk
  • 日志随着时间越长价值越低,因此超过一定时间日志需要进行冷备份
  • 日志需要提前明确日志的规范,可以方便查找与统计

你可能感兴趣的:(java基础知识)