1.ACID
原⼦性、⼀致性、事务性、持久性
2.数据库的三范式?
第⼀范式:数据库表的每⼀个字段都是不可分割的
第⼆范式:数据库表中的⾮主属性只依赖于主键
第三范式:不存在⾮主属性对关键字的传递函数依赖关系
3在Mysql中,使⽤以下代码查询显示前50⾏:
SELECT*FROM LIMIT 0,50;
4.Mysql中有哪⼏种锁?
MyISAM⽀持表锁,InnoDB⽀持表锁和⾏锁,默认为⾏锁
表级锁:开销⼩,加锁快,不会出现死锁。锁定粒度⼤,发⽣锁冲突的概率最⾼,并发量最低
⾏级锁:开销⼤,加锁慢,会出现死锁。锁⼒度⼩,发⽣锁冲突的概率⼩,并发度最⾼
4.1如果⼀个表有⼀列定义为TIMESTAMP,将发⽣什么?
每当⾏被更改时,时间戳字段将获取当前时间戳。
5.Mysql数据优化
1. 优化数据类型
1. 避免使⽤NULL,NULL需要特殊处理,⼤多数时候应该使⽤NOT NULL,或者使⽤⼀个
特殊的值,如0,-1作为默认值。
2. 仅可能使⽤更⼩的字段,MySQL从磁盘读取数据后是存储到内存中的,然后使⽤cpu
周期和磁盘I/O读取它,这意味着越⼩的数据类型占⽤的空间越⼩.
2. ⼩⼼字符集转换
START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summmary=@A WHERE type=1;
COMMIT;
客户端或应⽤程序使⽤的字符集可能和表本身的字符集不⼀样,这需要MySQL在运⾏过程
中隐含地进⾏转换,此外,要确定字符集如UTF-8是否⽀持多字节字符,因此它们需要更
多的存储空间。
3. 优化count(my_col)和count(*)
4. 优化⼦查询
遇到⼦查询时,MySQL查询优化引擎并不是总是最有效的,这就是为什么经常将⼦查询转换为
连接查询的原因了,优化器已经能够正确处理连接查询了,当然要注意的⼀点是,确保连接表
(第⼆个表)的连接列是有索引的,在第⼀个表上MySQL通常会相对于第⼆个表的查询⼦集进⾏⼀
次全表扫描,这是嵌套循环算法的⼀部分。
5. 优化UNION
在跨多个不同的数据库时使⽤UNION是⼀个有趣的优化⽅法,UNION从两个互不关联的表中返
回数据,这就意味着不会出现重复的⾏,同时也必须对数据进⾏排序,我们知道排序是⾮常耗
费资源的,特别是对⼤表的排序。
UNION ALL可以⼤⼤加快速度,如果你已经知道你的数据不会包括重复⾏,或者你不在乎是否
会出现重复的⾏,在这两种情况下使⽤UNION ALL更适合。此外,还可以在应⽤程序逻辑中采
⽤某些⽅法避免出现重复的⾏,这样UNION ALL和UNION返回的结果都是⼀样的,但UNION
ALL不会进⾏排序。
6.truncate delete drop的区别:
drop(DDL语句):是不可逆操作,会将表所占⽤空间全部释放掉;
truncate(DDL语句):只针对于删除表的操作,在删除过程中不会激活与表有关的删除触发器
并且不会把删除记录放在⽇志中;当表被truncate后,这个表和索引会恢复到初始⼤⼩;
delete(DML语句):可以删除表也可以删除⾏,但是删除记录会被计⼊⽇志保存,⽽且表空间
⼤⼩不会恢复到原来;
执⾏速度:drop>truncate>delete
7.Hashmap什么时候进⾏扩容呢?
当hashmap中的元素个数超过数组⼤⼩loadFactor时,就会进⾏数组扩容,loadFactor的默认值
为0.75,也就是说,默认情况下,数组⼤⼩为16,那么当hashmap中元素个数超过160.75=12
的时候,就把数组的⼤⼩扩展为216=32,即扩⼤⼀倍,然后重新计算每个元素在数组中的位
置,⽽这是⼀个⾮常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预
设元素的个数能够有效的提⾼hashmap的性能。⽐如说,我们有1000个元素new
HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上⾯annegu已经说过,即使
是1000,hashmap也⾃动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为
0.751000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最
合适,既考虑了&的问题,也避免了resize的问题。
8.LinkedHashMap的实现原理?
LinkedHashMap也是基于HashMap实现的,不同的是它定义了⼀个Entry header,这个
header不是放在Table⾥,它是额外独⽴出来的。LinkedHashMap通过继承hashMap中的
Entry,并添加两个属性Entry before,after,和header结合起来组成⼀个双向链表,来实现按插⼊
顺序或访问顺序排序。LinkedHashMap定义了排序模式accessOrder,该属性为boolean型变
量,对于访问顺序,为true;对于插⼊顺序,则为false。⼀般情况下,不必指定排序模式,其
迭代顺序即为默认为插⼊顺序。
9.什么是迭代器(Iterator)?
Iterator接⼝提供了很多对集合元素进⾏迭代的⽅法。每⼀个集合类都包含了可以返回迭代 器实
例的迭代⽅法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调⽤集合的
remove(Object Obj)删除,可以通过迭代器的remove()⽅法删除。
10.Comparable和Comparator接⼝是⼲什么的?列出它们的区别。
Java提供了只包含⼀个compareTo()⽅法的Comparable接⼝。这个⽅法可以个给两个对象排
序。具体来说,它返回负数,0,正数来表明输⼊对象⼩于,等于,⼤于已经存在的对象。
Java提供了包含compare()和equals()两个⽅法的Comparator接⼝。compare()⽅法⽤来给两个
输⼊参数排序,返回负数,0,正数表明第⼀个参数是⼩于,等于,⼤于第⼆个参数。equals()
⽅法需要⼀个对象作为参数,它⽤来决定输⼊参数是否和comparator相等。只有当输⼊参数也
是⼀个comparator并且输⼊参数和当前comparator的排序结果是相同的时 候,这个⽅法才返
回true。
11.简述java垃圾回收机制?
在java中,程序员是不需要显示的去释放⼀个对象的内存的,⽽是由虚拟机⾃⾏执⾏。在JVM
中,有⼀个垃圾回收线程,它是低优先级的,在正常情况下是不会执⾏的,只有在虚拟机空闲
或者当前堆内存不⾜时,才会触发执⾏,扫⾯那些没有被任何引⽤的对象,并将它们添加到要
回收的集合中,进⾏回收。
12.如何判断⼀个对象是否存活?(或者GC对象的判定⽅法)
判断⼀个对象是否存活有两种⽅法:
1. 引⽤计数法
所谓引⽤计数法就是给每⼀个对象设置⼀个引⽤计数器,每当有⼀个地⽅引⽤这个对象
时,就将计数器加⼀,引⽤失效时,计数器就减⼀。当⼀个对象的引⽤计数器为零时,说
明此对象没有被引⽤,也就是“死对象”,将会被垃圾回收.
引⽤计数法有⼀个缺陷就是⽆法解决循环引⽤问题,也就是说当对象A引⽤对象B,对象B
⼜引⽤者对象A,那么此时A,B对象的引⽤计数器都不为零,也就造成⽆法完成垃圾回收,
所以主流的虚拟机都没有采⽤这种算法。
2. 可达性算法(引⽤链法)
该算法的思想是:从⼀个被称为GC Roots的对象开始向下搜索,如果⼀个对象到GC Roots
没有任何引⽤链相连时,则说明此对象不可⽤。
在java中可以作为GC Roots的对象有以下⼏种:
虚拟机栈中引⽤的对象
⽅法区类静态属性引⽤的对象
⽅法区常量池引⽤的对象
本地⽅法栈JNI引⽤的对象
虽然这些算法可以判定⼀个对象是否能被回收,但是当满⾜上述条件时,⼀个对象⽐不⼀定会
被回收。当⼀个对象不可达GC Root时,这个对象并不会⽴⻢被回收,⽽是出于⼀个死缓的阶
段,若要被真正的回收需要经历两次标记.
如果对象在可达性分析中没有与GC Root的引⽤链,那么此时就会被第⼀次标记并且进⾏⼀次筛
选,筛选的条件是是否有必要执⾏finalize()⽅法。当对象没有覆盖finalize()⽅法或者已被虚拟机
调⽤过,那么就认为是没必要的。 如果该对象有必要执⾏finalize()⽅法,那么这个对象将会放
在⼀个称为F-Queue的对队列中,虚拟机会触发⼀个Finalize()线程去执⾏,此线程是低优先级
的,并且虚拟机不会承诺⼀直等待它运⾏完,这是因为如果finalize()执⾏缓慢或者发⽣了死
锁,那么就会造成F-Queue队列⼀直等待,造成了内存回收系统的崩溃。GC对处于F-Queue中
的对象进⾏第⼆次被标记,这时,该对象将被移除”即将回收”集合,等待回收。
13.finalize()⽅法什么时候被调⽤?析构函数(finalization)的⽬的是什么?
垃圾回收器(garbage colector)决定回收某对象时,就会运⾏该对象的finalize()⽅法 但是在Java
中很不幸,如果内存总是充⾜的,那么垃圾回收可能永远不会进⾏,也就是说filalize() 可能永远
不被执⾏,显然指望它做收尾⼯作是靠不住的。 那么finalize()究竟是做什么的呢? 它最主要的
⽤途是回收特殊渠道申请的内存。Java程序有垃圾回收器,所以⼀般情况下内存问题不⽤程序
员操⼼。但有⼀种JNI(Java Native Interface)调⽤non-Java程序(C或C++), finalize()的⼯作
就是回收这部分的内存。
14.分代
分代是Java垃圾收集的⼀⼤亮点,根据对象的⽣命周期⻓短,把堆分为3个代:Young,Old和
Permanent,
Young(年轻代)
年轻代分三个区。⼀个Eden区,两个Survivor区。⼤部分对象在Eden区中⽣成。当Eden区满
时,还存活的对象将被复制到Survivor区 (两个中的⼀个),当这个Survivor区满时,此区的
存活对象将被复制到另外⼀个Survivor区,当这个Survivor去也满了的时候,从第⼀ 个Survivor
区复制过来的并且此时还存活的对象,将被复制“年⽼区(Tenured)”
Tenured(年⽼代)
年⽼代存放从年轻代存活的对象。⼀般来说年⽼代存放的都是⽣命期较⻓的对象。
Perm(持久代)
⽤于存放静态⽂件,如今Java类、⽅法等。持久代对垃圾回收没有显著影响,但是有些应⽤可
能动态⽣成或者调⽤⼀些class,例如Hibernate等, 在这种时候需要设置⼀个⽐较⼤的持久代
空间来存放这些运⾏过程中新增的类。持久代⼤⼩通过-XX:MaxPermSize=进⾏设置。
Gc的基本概念
gc分为full gc 跟 minor gc,当每⼀块区满的时候都会引发gc。
Scavenge GC ⼀般情况下,当新对象⽣成,并且在Eden申请空间失败时,就触发了Scavenge
GC,堆Eden区域进⾏GC,清除⾮存活对象,并且把尚且存活的对象移动到Survivor区。然后
整理Survivor的两个区。
Full GC 对整个堆进⾏整理,包括Young、Tenured和Perm。Full GC⽐Scavenge GC要慢,因
此应该尽可能减少Full GC。有如下原因可能导致Full GC:
Tenured被写满
Perm域被写满
System.gc()被显示调⽤
上⼀次GC之后Heap的各域分配策略动态变化
简述java内存分配与回收策率以及Minor GC和Major GC
对象优先在堆的Eden区分配。
⼤对象直接进⼊⽼年代.
⻓期存活的对象将直接进⼊⽼年代.
当Eden区没有⾜够的空间进⾏分配时,虚拟机会执⾏⼀次Minor GC.Minor Gc通常发⽣在新⽣代的Eden区,在这个区的对象⽣存期短,往往发⽣Gc的频率较⾼,回收速度⽐较
快;Full Gc/Major GC 发⽣在⽼年代,⼀般情况下,触发⽼年代GC的时候不会触发MinorGC,但是通过配置,可以在Full GC之前进⾏⼀次Minor GC这样可以加快⽼年代的回收速
度。
JVM的永久代中会发⽣垃圾回收么?
垃圾回收不会发⽣在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full
GC)。
(注:Java8中已经移除了永久代,新加了⼀个叫做元数据区的native内存区)
java中垃圾收集的⽅法有哪些?
标记-清除:这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统⼀回收。这种⽅法很简单,但是会有两个主要问题:1.效率不⾼,标记和清除的效率都很低;2.会产⽣⼤量不连续的内存碎⽚,导致以后程序在分配较⼤的对象时,由于没有
充⾜的连续内存⽽提前触发⼀次GC动作。
复制算法:为了解决效率问题,复制算法将可⽤内存按容量划分为相等的两部分,然后每次只使⽤其中的⼀块,当⼀块内存⽤完时,就将还存活的对象复制到第⼆块内存上,然后⼀次性清楚完第⼀块内存,再将第⼆块上的对象复制到第⼀块。但是这种⽅式,内存的代价太⾼,每次基本上都要浪费⼀般的内存。 于是将该算法进⾏了改进,内存区域不再是按照1:1去划分,⽽是将内存划分为8:1:1三部分,较⼤那份内存交Eden区,其余是两块较⼩的内存区叫Survior区。每次都会优先使⽤Eden区,若Eden区满,就将对象复制到第⼆块内存区上,然后清除Eden区,如果此时存活的对象太多,以⾄于Survivor不够时,会将这些对象通过分配担保机制复制到
⽼年代中。(java堆⼜分为新⽣代和⽼年代)
标记-整理:该算法主要是为了解决标记-清除,产⽣⼤量内存碎⽚的问题;当对象存活率较⾼时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动
到⼀端,然后清除掉端边界以外的对象,这样就不会产⽣内存碎⽚了。
分代收集 :现在的虚拟机垃圾收集⼤多采⽤这种⽅式,它根据对象的⽣存周期,将堆分为新⽣代和⽼年代。在新⽣代中,由于对象⽣存期短,每次回收都会有⼤量对象死去,那么这时就采⽤
复制算法。⽼年代⾥的对象存活率较⾼,没有额外的空间进⾏分配担保;
java中会存在内存泄漏吗,请简单描述。
会理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被⼴泛使⽤于服务器端编程的⼀个重要原因);然⽽在实际开发中,可能会存在⽆⽤但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发⽣。例如。⾃⼰实现堆载的数据结构时有可能会出现内存泄露 或者hibernate的Session(⼀级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然⽽这些对象中可能存在⽆⽤的垃圾对象,如果不及时关闭(close)或清空(flush)⼀级缓存就可能导致内存泄露。
java类加载过程?
java类加载需要经历⼀下7个过程:
1. 加载
加载是类加载的第⼀个过程,在这个阶段,将完成⼀下三件事情:
通过⼀个类的全限定名获取该类的⼆进制流。
将该⼆进制流中的静态存储结构转化为⽅法去运⾏时数据结构。
在内存中⽣成该类的Class对象,作为该类的数据访问⼊⼝。
2. 验证
验证的⽬的是为了确保Class⽂件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四种验证:
⽂件格式验证:验证字节流是否符合Class⽂件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被⽀持的类型.
元数据验证:对字节码描述的信息进⾏语义分析,如这个类是否有⽗类,是否集成了不被继承的类等。
字节码验证:是整个验证过程中最复杂的⼀个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对⽅法体的验证。如:⽅法中的类型转换是否正确,跳转指令是正确等。
符号引⽤验证:这个动作在后⾯的解析过程中发⽣,主要是为了确保解析动作能正确执⾏。
3. 准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在⽅法区中进
⾏分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象
⼀起分配在Java堆中。
public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。
4. 解析
该阶段主要完成符号引⽤到直接引⽤的转换动作。解析动作并不⼀定在初始化动作完成之
前,也有可能在初始化之后。
5. 初始化
初始化时类加载的最后⼀步,前⾯的类加载过程,除了在加载阶段⽤户应⽤程序可以通过
⾃定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正
开始执⾏类中定义的Java程序代码。
6. 使⽤
7. 卸载
简述java类加载机制?
虚拟机把描述类的数据从Class⽂件加载到内存,并对数据进⾏校验,解析和初始化,最终形成
可以被虚拟机直接使⽤的java类型。
什么是类加载器,类加载器有哪些?
实现通过类的权限定名获取该类的⼆进制字节流的代码块叫做类加载器。
主要有⼀下四种类加载器
1. 启动类加载器(Bootstrap ClassLoader)⽤来加载java核⼼类库,⽆法被java程序直接引
⽤。
2. 扩展类加载器(extensions class loader):它⽤来加载 Java 的扩展库。Java 虚拟机的实现会
提供⼀个扩展库⽬录。该类加载器在此⽬录⾥⾯查找并加载 Java 类。
3. 系统类加载器(system class loader):它根据 Java 应⽤的类路径(CLASSPATH)来加
载 Java 类。⼀般来说,Java 应⽤的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()来获取它。
4. ⽤户⾃定义类加载器,通过继承 java.lang.ClassLoader类的⽅式实现。
类加载器双亲委派模型机制?
当⼀个类收到了类加载请求时,不会⾃⼰先去加载这个类,⽽是将其委派给⽗类,由⽗类去加
载,如果此时⽗类不能加载,反馈给⼦类,由⼦类去完成类的加载。