每天学习一点小知识
1.类加载系统
1.1.类加载器:每个类加载器都有自己的内存,每个类的加载器不同,即使是同一个类,加载的字节码也不同;
类加载器有哪些:
- BootStrapClassLoader
- ExtClassLoader
- AppClassLoader
- 自定义类加载器
1.2 类加载机制:双亲委派模型
行为特征:向上询问,向下委派;
优点:可以保证同一个类只能被同一个类加载器加载一次,保证了类体系的健壮性;
缺点:需要一些CPU资源,影响效率;且对不同项目的包名,类名相同的类,无法实现正确加载;著名的tomcat框架就打破了双亲委派机制,实现了对不同项目中,但同一个签名的两个类的加载;
1.3 类的加载过程
- 查找类
- 读取类
- 连接:校验->准备->解析
- 初始化类->主动使用/被动使用: 被动使用时是不会初始化类的!!!
类的被动使用:通过调用子类的对象,该对象来自于父类;此时两个类都会被加载,但是子类属于被动加载,不执行初始化阶段,此时子类的静态代码块也就不执行了(静态代码块的执行时机在加载过程中的初始化阶段);
1.4 类的加载方式:
1.new对象,通过构造方法加载类;
2.通过反射加载对象,例如class.forName()
3.通过ClassLoader实例的loadClass()方法来加载
这三种加载方式分为静态加载和动态加载,前两个是静态加载:如果找不到要加载的类,抛出的是NoClassDefFoundError,使用的类都是当前类加载器,即应用程序上下文加载器;第三个是动态加载:如果在运行环境中找不到要初始化的类,抛出的是ClassNotFoundException,使用的加载器是用户指定的类加载器;
class.forName() 与 classLoader.loadClass() 的主要区别:方法参数里装载的对象不一样,前者装载的对象已经初始化,而后者连接过程都还没走完,更别提初始化了
1.5 如何自定义类加载器
直接或间接地继承ClassLoader类并重写相关方法
2.接口的默认方法
JDK1.8之前,接口里的方法都是抽象的,如果要实现某个接口,就必须重写接口里的所有方法;在实际开发里,实现的接口里不一定要用到所有的方法;因此在JDK1.8之后,接口里除了抽象方法,还有默认的实现方法,这种方法实现接口的实现类里,不一定要重写,这样我们就可以按照业务的需求,来写所需要的方法,减少了无用的代码量;
3.JVM调优
逃逸分析
一种用于java堆内存使用的算法;随着技术发展,对象已经不一定要存在堆上,也有可能存在栈里,而逃逸分析就是减少java对象在堆里面的分配;
逃逸对象的定义: 当一个对象在方法内创建,又没有被外界引用,则称该对象未逃逸;这种对象如果内存较小,随着逃逸分析的开启,会把内存分配给栈上;
当对象分配在堆时,GC操作会导致STW,程序会被暂停;并且,如果对象没有进入TLAB区时,会被加锁,同样导致了效率的降低;但是对象分配在栈时,不会触发GC操作,而且对象会自行随着栈帧销毁,响应的效率会提高许多甚至几十倍;
设置逃逸分析参数: -XX:+DoEscapeAnalysis ,在JDK8里默认是打开逃逸分析的,想要关闭,把参数里的加号改为减号即可;
同步锁消除: 由于同步锁的开销太大,如果编译期发现一个对象没有发生逃逸,那么就会移除该对象的同步锁;
标量替换: 标量是指一个无法再分解的数据,例如java中的原始数据类型;相对的,还能分解的数据称为聚合量,比如java中的对象;在JIT阶段,经过逃逸分析,发现一个对象不会逃逸的话,且该对象可以被标量替换,那么可以不直接创建该对象,而是使用基本类型来替代对象,减少了开销;
4.MyBatis的缓存机制(牺牲空间,换取时间)
一级缓存:
MyBatis框架内置了一级缓存,也称为会话缓冲,即SqlSession;它会保存查询语句相同的查询结果,等到下次查一样的语句时,就会从缓冲直接拿结果,不会再去数据库查询;一级缓存默认开启,无法关闭;
当出现以下情况时,都会把会话缓存都清空:
- sqlSession通过调用方法clearCache();
- 当发生了数据的增,删,改时,都会清除会话缓存,即使数据并没有真的发生变化;
二级缓存:
二级缓存的应用优先级高于一级缓存,myBatis先从二级缓存查数据,没找到再去一级缓存,还没找到,才到数据库里查数据;
二级缓存也称为nameSpace缓存,是作用于某个nameSpace的,即某个mapper;二级缓存默认全局开启,但是namespace默认未开启;如果要开启namespace的二级缓存,需在某个mapper的xml文件里添加标签:<cache/>,表示当前XML中所有查询都开启了二级缓存;如果某个查询不需要缓存,在xml文件里配置useCache="false",可以关闭缓存;
当启用二级缓存,每次查询时,日志会出现Cache Hit Ratio的记录;
二级缓存的数据是存在磁盘上的,需要查询结果的数据类型实现序列化接口,如果没实现接口,程序会直接报错;
二级缓存和一级缓存一样,只要执行了任何增,删,改的写操作,都会清除缓存;
MyBatis小结:
MyBatis不管是一级还是二级缓存,只要发生了写操作,就会清除缓存;而且这种机制我们还无法改变,实际开发中很少使用到MyBatis的缓存机制;