Java 在new 一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。
加载并初始化类完成后,再进行对象的创建工作。我们先假设是第一次使用该类,这样的话new 一个对象就可以分为两个过程:加载并初始化类和创建对象。
#类加载过程(第一次使用该类)
Java是使用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下它的工作过程:双亲委派模型的工作过程是:如果一个类加载器(ClassLoader) 收到了类加载器的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:能够有效确保一个类的全局性,当程序中出现多个限定名相同的类事,类加载器在执行加载时,始终只会加载其中的某一个类。
1、加载
由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转化为一个与目标类型对应的java.lang.Class对象实例
2、验证
格式验证:验证是否符合class文件规范
语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;
确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)
操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在
MySQL三层架构
第一层:客户端,比如连接处理、授权认证、安全等
第二层:包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
第三层:存储引擎。存储引擎负责MySQL中数据等存储和提取。
MySQL会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引等。对于SELECT语句,在解析查询之前,服务器会先检查查询缓存(Query Cache),如果能够在其中找到对应的查询,服务器就不必再执行查询解析、优化和执行的整个过程。
共享锁(shared lock)和排他锁(exclusive lock),也叫读锁(read lock)和写锁(write lock).
所谓的锁策略,就是在锁的开销和数据的安全性之间寻求平衡。
表锁(table lock)
MySQL中最基本的锁策略,并且是开销最小的策略。一个用户在对表进行写操作(插入,删除,更新等)前,需要先获取写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他读取的用户才能获取读锁,读锁之间是不相互阻塞的。 另外写锁比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面。
行级锁(row lock)
行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销)。在InnoDB和XtraDB,以及其他一些存储引擎中实现了行级锁。行级锁只在存储引擎层实现,而MySQL服务器层没有实现。服务器层完全不了解存储引擎中的锁实现。
事务
事务就是一组原子性的SQL查询,或者说一个独立的工作单元。事务内的语句,要么全部执行成功,要么全部执行失败。ACID 原子性、一致性、隔离性、持久性
隔离级别
READ UNCOMMITTED(未提交读)
事务中的修改,即使没有提交,对其他事物也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read).
READ COMMITTED(提交读)
大多数数据库系统的默认隔离级别都是READ COMMITTED(但是MySQL不是)。不可重复读
REPEATABLE READ(repeatable)(可重复读)
解决脏读未提。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但不可解决幻读的问题。InnoDB和XtraDB 存储引擎通过多版本并发控制(MVCC)解决了幻读读问题。 该隔离级别是MYSQL 默认事务隔离级别
SERIALIZABLE(可串行化)
最高的隔离级别。强制事务串行执行,避免前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。
死锁
指两个或者多个事务在同一个资源上相互占用,并请求锁定对方占用的资源。 为解决这个问题,数据库系统是心啊了各种死锁检测和死锁超时机制。InnoDB目前处理死锁的方法是:将持有最少行级排他锁的事务进行回滚。
死锁的产生有双重原因:有些是因为真正的数据冲突,有些则完全是由于存储引擎的实现方式导致的。
死锁发生以后,只有部分或者完全回滚其中一个事务,才能打破死锁。
事务日志:
帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时至需要修改其内存拷贝,再把该修改行为记录到持久化在硬盘上的事务日志中,而不是每次都将修改的数据本身持久化到磁盘。事务日志采用的是追加的方式,通常称之为预写式日志,修改数据需要写两次磁盘。
MySQL中的事务:
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
自动提交(AUTOCOMMIT). MySQL默认采用自动提交模式。
隐式和显式锁定
InnoDB采用的是两阶段锁定协议。在事务执行过程中,随时都可以执行锁定,锁只有在执行COMMIT 或者ROLLBACK的时候才会释放,并且所有的锁是在同一时刻被释放。前面描述的锁定都是隐式锁定,InnoDB会根据隔离级别在索要的时候自动加锁。
MySQl也支持LOCK TABLES和UNLOCK TABLES语句,这是在服务器层实现的,和存储引擎无关。
多版本并发控制
MySQL的大多数事务型存储引擎一般都同时实现了多版本并发控制(MVCC)。可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现方式不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。MVCC的实现通过保存数据在某个时间点的快照来实现。根据事务开始时间的不同,每个事务对同一张表,同一时刻看到的数据可能不一样。
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的版本号会作为事务的版本号。
在REPEATABLE READ隔离级别下,MVCC如何操作的
SELECT
InnoDB会根据一下两个条件检查每行记录
a. InnoDB只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
B.行的三次版本要么未定义,要么大雨当前事务版本号。这样可以确保事务读取到的行,在事务开始之前未被删除。
INSERT InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
DELETE InnoDB为删除的每一行保存当前系统版本号作为行删除标示。
UPDATE InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标示。
保存这两个额外系统版本号,使大多数读操作不用加锁。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作
MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。READ UNCOMMITTED总是读取最新的数据行,而不符合当前事务版本的数据行,SERIALIZABLE则会对所有读取的行都加锁。