##数据库
#三个范式
属性唯一(确
保每列保持原子性) 数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。
记录唯一(确保表中的每列都和主键相关) 数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字
表唯一(确保每列都和主键列直接相关,而不是间接相关) 在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。所谓传递函数依赖,指的是如果存在"A → B → C"的决定关系,则C传递函数依赖于A。因此,满足第三范式的数据库表应该不存在如下依赖关系: 关键字段 → 非 关键 字段x → 非关键字段y
#SQL优化
总是使用索引的第一个列
使用where而不是having
多使用commit
使用truncate而不是delete
#Servlet
#运行过程
Servlet由web服务器调用,收到客户端的Servlet访问请求后
1.检查是否已经装载并创建了该Servlet的实例对象,是转4,不是转2
2.装载并创建该Servlet的一个实例对象
3.调用init()方法
4.创建一个用于封装HttpSevrletRequest对象和一个代表Http响应消息的HttpServletRespouse对象,调用Service()方法,将这两个对象传递出去
5.web应用程序停止或者重启之前,卸载Servlet,并调用其destroy()方法
#JAVA线程安全
#线程操作某个对象
多个线程同时读写某个内存数据时,就会产生多线程并发问题,涉及到三个特性:原子性,有序性,可见性
(1) 从主存复制变量到当前工作内存 (read and load)
(2) 执行代码,改变共享变量值 (use and assign)
(3) 用工作内存数据刷新主存相关内容 (store and write)
##synchronized关键字
synchronized(锁){
临界区代码
}
public synchronized void function() {
临界区代码
}
//每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的
//每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待cpu的调度
//一个线程执行临界区代码过程
1 获得同步锁
2 清空工作内存
3 从主存拷贝变量副本到工作内存
4 对这些变量计算
5 将变量从工作内存写回到主存
6 释放锁
//synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。
##volatile关键字
volatile只能保证多线程的内存可见性,不能保证多线程的执行有序性
任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,
所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的.
//要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
1)对变量的写操作不依赖于当前值。
2)该变量没有包含在具有其他变量的不变式中
##JVM内存划分
1.程序计数器
每一个Java线程都有一个程序计数器来用于保存程序执行到当前方法的哪一个指令。
2.线程栈
线程的每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息
3.本地方法栈
4.堆
每个线程的栈都是该线程私有的,堆则是所有线程共享的,new一个对象被分配到堆中
jvm的gc都是按代收集,
堆区大致被分为三大块:新生代,旧生代,持久代(虚拟的);新生代又分为eden区,s0区,s1区。
新建一个对象时,基本小的对象,生命周期短的对象都会放在新生代的eden区中,
eden区满时,有一个小范围的gc(minor gc)
整个新生代满时,会有一个大范围的gc(major gc),将新生代里的部分对象转到旧生代里。
5.方法区
永久代,包括常量池、字段描述、方法描述
6.常量池
##垃圾回收器
引用计数法 (Reference Counting)
对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1,当引用失效时,引用计数器就减 1。只要对象 A 的引用计数器的值为 0,则对象 A 就不可能再被使用。
缺点:无法处理循环互相引用
标记-清除算法 (Mark-Sweep)
标记阶段和清除阶段
在标记阶段首先通过根节点,标记所有从根节点开始的较大对象
未被标记的对象就是未被引用的垃圾对象
在清除阶段,清除所有未被标记的对象。
缺点:存在大量的空间碎片,回收后的空间是不连续的
复制算法 (Copying)
将现有的内存空间分为两快,每次只使用其中一块
在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成回收。
优点:真正需要垃圾回收的时刻,复制算法的效率是很高的
确保回收后的内存空间是没有碎片的
缺点:将系统内存折半
(高效的前提是建立在存活对象少,垃圾对象多)——>适用年轻代
Java 的新生代串行垃圾回收器中使用了复制算法的思想
新生代分为 eden 空间、from 空间、to 空间
from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。
eden 空间中的存活对象会被复制到未使用的 survivor 空间中
正在使用的 survivor 空间 中的年轻对象也会被复制到 to 空间中
eden 空间和 from 空间中的剩余对象就是垃圾对象,可以直接清空
优点:改进的复制算法既保证了空间的连续性,又避免了大量的内存空间浪费。
标记-压缩算法 ——>适用老年代
标记——清除算法的优化
从根节点开始对所有可达对象做一次标记
是将所有的存活对象压缩到内存的一端,清理边界外所有的空间
优点:避免了碎片的产生,又不需要两块相同的内存空间,性价比比较高。
增量算法 (Incremental Collecting)
在垃圾回收过程中,应用软件将处于一种 CPU 消耗很高的状态
程序所有的线程都会挂起,暂停一切正常的工作,等待垃圾回收的完成。
如果一次性将所有的垃圾进行处理,需要造成系统长时间的停顿,
思想:让垃圾收集线程和应用程序线程交替执行
优点:减少系统的停顿时间
缺点:线程切换和上下文切换的消耗,使得垃圾回收总体成本上升,造成系统吞吐量下降
分代 (Generational Collecting)
思想:不同阶段最优的方式是使用合适的算法用于本阶段的垃圾回收
将内存区间根据对象的特点分成几块,根据每块内存区间的特点,使用不同的回收算法,以提高垃圾回收的效率。
将所有的新建对象都放入称为年轻代的内存区域,年轻代的特点是对象会很快回收,因此,在年轻代就选择效率较高的复制算法。
当一个对象经过几次回收后依然存活,对象就会被放入称为老生代的内存空间,对老年代的回收使用标记-压缩算法,提高垃圾回收效率。
###从不同角度分析垃圾收集器,可以将其分为不同的类型。
1.按线程数分,可以分为串行垃圾回收器和并行垃圾回收器。
2.按照工作模式分,可以分为并发式垃圾回收器和独占式垃圾回收器
3.按碎片处理方式可分为压缩式垃圾回收器和非压缩式垃圾回收器
4.按工作的内存区间,又可分为新生代垃圾回收器和老年代垃圾回收器
###评价垃圾处理器的指标
吞吐量:指在应用程序的生命周期内,应用程序所花费的时间和系统总运行时间的比值。
垃圾回收器负载:和吞吐量相反,垃圾回收器负载指来记回收器耗时与系统运行总时间的比值。
停顿时间:指垃圾回收器正在运行时,应用程序的暂停时间。
垃圾回收频率:指垃圾回收器多长时间会运行一次。
(通常增大堆空间可以有效降低垃圾回收发生的频率,但是可能会增加回收产生的停顿时间。)
反应时间:指当一个对象被称为垃圾后多长时间内,它所占据的内存空间会被释放。
堆分配:不同的垃圾回收器对堆内存的分配方式可能是不同的。一个良好的垃圾收集器应该有一个合理的堆内存区间划分。