jvm类加载器及类加载顺序
加载器
1)BootstrapClassLoader(启动类加载器)
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,加载System.getProperty(“sun.boot.class.path”)所指定的路径或jar。
2)ExtensionClassLoader(标准扩展类加载器)
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包。载System.getProperty(“java.ext.dirs”)所指定的路径或jar。
3)AppClassLoader(系统类加载器)
负责记载classpath中指定的jar包及目录中class
4)CustomClassLoader(自定义加载器)
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现
类加载器的顺序
1)加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
2)在加载类时,每个类加载器会将加载任务上交给其父,如果其父找不到,再由自己去加载。
3)Bootstrap Loader(启动类加载器)是最顶级的类加载器了,其父加载器为null。
spi
spi能带来的好处:
不需要改动源码就可以实现扩展,解耦。
实现扩展对原来的代码几乎没有侵入性。
只需要添加配置就可以实现扩展,符合开闭原则。
关键信息:ServiceLoader可以获取扩展实现的类实例
写好接口及实现类;
在resources目录下新建META-INF/services目录,并且在这个目录下新建一个与上述接口的全限定名一致的文件,在这个文件中写入接口的实现类的全限定名:
https://www.cnblogs.com/jy107...
B+tree
B树特点: 每个节点都存储了子节点的指针和本节点的键值及数据; 精确查找ok,范围查找有较多的io操作,范围越大,性能越低;
B+ 树特点 1.非叶子节点只存储键值信息。 2.所有叶子节点之间都有一个双向链指针。(提升插入操作效率) 3.数据记录都存放在叶子节点中。
sql优化
slow_query配置 查看慢sql日志
最左匹配
explain 执行计划
binlog主从同步延迟
硬件问题提升硬件
分库分表
- 针对数据库方面的拆分主要是结合分库分表来实现,即对于分库,则根据业务内聚性,拆分为几个相对独立的子服务,各个子服务使用独立的数据库分开部署,则可以将写操作分散到各个数据库,各个数据库的复制流量相对较小,从而通过分而治之的方法来降低整体的延迟
线程池**
Java线程池参数
corePoolSize 线程池核心线程大小
maximumPoolSize 线程池最大线程数量
keepAliveTime 空闲线程存活时间
Timeunit 空闲线程存活时间单位
workQueue 工作队列
①ArrayBlockingQueue
②LinkedBlockingQuene
③SynchronousQuene
④PriorityBlockingQueue
threadFactory 线程工厂
handler 拒绝策略
jvm调参与内存问题排查
volatile
1.保证有序性
2.保证可见性
3.不保证原子性
场景:
1)对变量的写操作不依赖于当前值
2)该变量没有包含在具有其他变量的不变式中
保证对变量操作的原子性才可使用volatile
gc
回收哪些内存
可达性分析算法:每个对象都被对象的引用,root object
两种回收算法
标记删除法、标记整理法
内存模型 minor gc为年轻代区域回收 majorgc 为老年代区域回收
常见垃圾回收器CMS G1, jdk9后默认从CMS切换到G1
jvm内存模型
线程隔离:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)
线程共享:堆(Heap)、方法区(Method Area)
堆内存是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。
方法区主要是用来存放已被虚拟机加载的类相关信息,包括类信息、常量池(字符串常量池以及所有基本类型都有其相应的常量池)、运行时常量池
程序计数器记录各个线程执行的字节码的地址,例如,分支、循环、跳转、异常、线程恢复等都依赖于计数器
虚拟机栈是线程私有的内存空间,它和 Java 线程一起创建。
当创建一个线程时,会在虚拟机栈中申请一个线程栈,用来保存方法的局部变量、操作数栈、动态链接方法和返回地址等信息,并参与方法的调用和返回。
本地方法栈跟虚拟机栈的功能类似,虚拟机栈用于管理 Java 方法的调用,而本地方法栈则用于管理本地方法的调用。
但本地方法并不是用 Java 实现的,而是由 C 语言实现的。
每一个方法的调用都伴随着栈帧的入栈操作,方法的返回则是栈帧的出栈操作
单例
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {// 1
if (instance == null) {// 2
instance = new Singleton();// 3
}
}
}
return instance;
}
}
这里是主要是要理解为什么要使用volatile修饰变量才行,是因为:
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作,所以才不会出现对象还在创建就被其他线程拿去使用的情况,其他线程都会得到一个创建好的对象
使用内部类实现延迟加载
public class Singleton {
private Singleton() {}
private static class Holder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
// 外围类能直接访问内部类(不管是否是静态的)的私有变量
return Holder.instance;
}
}