数据库
- MySQL中的数据类型有哪些
- 整型数据类型:(UNSIGNED)TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT
- 浮点数据类型:FLOAT,DOUBLE,DECIMAL
- 字符串类型:CHAR,VARCHAR,TEXT,BLOB
- CHAR和VARCHAR之间的区别
- CHAR是定长的,并且会在长度小于声明长度的字符串的右边使用空格填充尾部,并且检索时去掉空格
- VARCHAR是变长的,在数据的开头使用1-2个字节保存数据长度,并且在尾部添加结束的标记
- CHAR,VARCAHR,TEXT之间的却别
- 主要是大小区别
- 数据库的三大范式是什么
- 第一范式:所有的树形列都是不可再分的原子属性
- 第二范式:存在一个候选码, 非主属性都完全依赖于该候选码,消除部分的函数依赖
- 第三范式:非主属性之间不会互相依赖,消除了传递依赖
- 什么是范式,什么是反范式
- 范式就是指符合一种级别的关系模式的集合,是构建数据库的规则
- 范式化的优点是,减少数据的冗余,更新操作更快,表占用的空间更少,缺点就是查询的时候需要关联表查询,索引也难以维护
- 反范式化的优点是,通过数据的冗余大大增强了数据的查询效率,减少了关联查询和索引的维护,缺点就是数据荣誉增加,数据维护成本更高
- 索引的分类,在物理结构上、约束类型上、数据实现上
- 索引是指对数据库表中的一列或者多列的数值进行排序的数据结构,用于快速访问数据库表中的特定信息
- 通过物理存储可以分为聚簇索引和非聚簇索引,聚簇索引表示索引的存储顺序和原数据行的存储顺序是一致的,非聚簇索引是指索引的存储顺序和原数据的存储顺序是不一致的
- 通过约束来分类,可以分为:普通索引、唯一索引、主键索引和组合索引
- 普通索引是指,索引的值可以重复和为空 ALTER TABLE table_name ADD INDEX index_name(column_name)
- 唯一索引是指,索引的值不可以重复,但是可以为空 ALTER TABLE table_name ADD UNIQUE index_name(column_name) CREATE UNIQUE INDEX index_name ON table_name(column_name)
- 主键索引是指,索引二等值不能重复,并且索引不能为空 ALTER TABLE table_name ADD PRIMARY KEY(column_name)
- 组合索引是指在多列的值上创建索引 ALTER TABLE table_name ADD INDEX index_name(column_name1,column_name2…)
- 通过数据结构可以分为,B树实现,哈希实现和位图实现
- 使用索引的优点和缺点
- 优点:
- 创建唯一索引可以保证数据的唯一性
- 加快数据检索的效率,加快分组和排序的效率
- 提高表之间的关联的速度,实现数据的参考完整性
- 可以使用隐藏优化器提高数据库的查询性能
- 缺点:
- 索引需要时间创建和维护,当数据量增加时,索引为需要增加和更新
- 索引的数据结构需要占用空间
- 优点:
- 使用索引的注意事项
- 使用唯一索引,有主意确定数据是哪一条
- 在经常用于查询、分组、排序和联合的字段上建立索引,有助于提高这些操作的效率
- 字段值较长的不适合作为索引,会影响索引建立的时间空间效率,可以考虑使用前缀索引
- 小表可以不用建立索引,数据量比较小的时候使用索引的优化效果不大
- 不能过多地建立索引,因为索引的创建和维护需要时间和空间
- 删除一些不需要和不经常使用的索引
- B+树索引和hash索引之间的区别是什么
- hash索引通过散列码将索引值散列到对应的哈希表中,通过哈希表查找到数据的物理存储位置,然后回表查询,B+树从根结点处查询到叶子节点进行数据的查找。
- hash索引无法避免回表,并且hash索引具有不稳定性,如果键值出现大量重复,会导致较多的hash冲突,从而降低索引性能,B+树索引支持范围查询和左前缀匹配,当满足索引覆盖和聚簇索引条件时可以不用回表查询。
- 为什么使用B+树而不使用二叉平衡树
- 二叉平衡树的查询效率为O(logn),它的查询效率会随着树二的高度而减小,当数据急剧增加时二叉树的高度也会增加,还需要通过左旋和右旋的方式维护二叉平衡树的平衡,由于索引文件保存在磁盘中,这就涉及很多的磁盘IO操作,会大大降低性能。
Java基础
- Java虚拟机由哪几部分组成
- Java虚拟机(Java Virtual Machine)是Java的运行环境,也是操作系统的一个应用程序,有自己的内存空间和生命周期
- 类加载器:根据全限定名称将Java的class字节码文件加载到运行数据区域的方法区
- 本地接口:与本地库交互,也是与其他编程语言交互的接口
- 运行时数据区域:JVM内存,包括内存堆和内存栈空间
- 执行引擎:执行class文件中的指令
- Java运行时数据区域的组成
- Java运行时数据区域分为程序计数器,虚拟机栈,本地方法栈,堆,方法区
- 程序计数器时线程独有的,可以看成时线程执行字节码文件的行号指示器,字节码解释器通过修改程序计数器的值来完成分支、、循环、跳转、异常处理、线程恢复等基本操作。JVM通过线程轮流切换并且分配处理器的时间来实现多线程,在同一时刻,每一个处理器只能执行一个线程的一条指令,因此为了在线程切换时能够恢复到线程执行的争取位置,需要使每一个线程拥有一个独占的程序计数器,每个程序计数器之间互不影响。
- 虚拟机栈是线程独有的,在栈中,在一个方法执行前会生成一个方法对应的栈帧,栈帧中保存着局部变量表、操作数表、动态链接信息、方法出口信息等,每一个方法对应的栈帧中的局部变量表的大小在编译器编译之后就确定了。对于虚拟机栈,存在两种内存限制,当线程请求的栈的大小超过虚拟机的限制范围之后会抛出 StackOverFlowError的异常,如果可以动态扩展而没有申请到内存空间,就会抛出OutOfMemoryError异常
- 本地方法栈:和虚拟机栈基本相同,不同之处是,虚拟机栈是为了线程的Java方法服务,本地方法栈是为了给虚拟机调用本地方法而服务的
- 堆:堆是虚拟机中线程共享的内存空间,是虚拟机中最大的一块内存,用于保存实例对象
- 方法区:线程共享的一块区域,用于保存常量、静态变量、类信息、编译器编译后的代码数据缓存
- 虚拟机中栈帧的组成
- 虚拟机中栈帧由:局部变量表、操作栈、动态链接和返回地址组成
- 局部变量表存储了局部变量和方参数的变量和值,在编译器编译字节码文件过程中通过指定方法的属性max_locals确定
- 操作栈是一个先进先出的栈,用于存放方法中计算的中间值或者临时变量,也在编译器编译源文件时在方法属性中通过max_satcks确定了大小
- 动态链接中存放着指向运行时常量池中的方法的引用,一般会指向当前栈帧对应的方法的引用以及需要调用的方法的引用
- 方法返回地址
- 堆和栈的区别
- 物理空间上:堆不是连续的
- 分配的时间:堆在运行时动态分配,栈在编译时期确定好大小
- 生命周期:堆在JVM启动时划分,所有的线程都可见,栈属于一个线程,生命周期和线程同步
- 存放内容:堆中存放对象和数组,栈中存放方法的栈帧(局部变量表、操作栈、动态链接和返回地址)
- 创建对象有哪些过程
- 类加载:JVM遇到一个new指令之后,会对指令参数进行检查是否对应常量池中的一个类的符号引用,并且检查这个类符号你引用,是否已经被加载、解析和初始化
- 内存分配:当类加载完毕之后,就能够确定对象的大小,JVM会根据GC回收所处的时期来指定内存分配算法
- 如果堆中的内存是连续的,使用指针碰撞法,根据对象的大小来移动指针
- 如果堆中的空间不连续,空闲的空间和已经被使用的空间交错存在时,就会使用一个空闲列表来记录空闲的内存区域,JVM从列表中找到一个能够容纳对象的大小的空闲区域,分配给对象,然后更新空闲列表
- 对象初始化:完成内存分配之后,会对对象的属性赋默认值,初始化对象头(哈希码、对象对应类、对象锁、偏向锁、对象大小等信息)、执行构造函数等
- 创建对象如何解决并发问题
- JVM分配内存时,可以使用CAS失败重试得方式保证并发性
- 在堆内存中按照线程来划分分配区域,预先为每一个线程分配一块区域,即TLAB(T和read Local Allocation Buffers)线程本地分配缓冲,当线程执行对象的创建时,现在TLAB中进行分配,在TLAB使用完后,需要分配新的TLAB时在使用同步锁定机制
- Java如何访问对象
- 通过指针进行访问:在栈中存放队中对象的地址,对象指向方法区中类数据的地址
- 通过句柄池进行访问:栈中存放对象和类数据句柄池(堆中)中的地址,句柄中存放实际对象数据的地址和类数据的地址
- 使用指针会更快,使用句柄会更稳定,当GC堆内存修改了之后,对象的地址如果改变了,只需要改变句柄池中的地址即可
- 对象在内存中时怎样存在的
- 对象的在内存中包括三个部分:对象头、实例数据、对齐填充
- 对象头中包含了:对象的hashcode,对象对应的类、对象的GC分代年龄(初始创建时在eden区,当精力多次GC没有回收,达到一定的GC分代年龄时会进入老年区)、对象是否为锁,对象偏向锁被持有的线程ID和持有时间
- 实例数据中存储了对象的属性和字段的值,包含从父类中继承的属性,会根据大小排序,其中继承父类的属性排在前面,因为父类被先加载
- 对齐填充
- 对象的在内存中包括三个部分:对象头、实例数据、对齐填充
- 什么是垃圾回收系统
- 在Java程序执行过程中,会产生一些失去引用指向的对象,这些对象就是无法访问的垃圾,Java虚拟机就会进行垃圾回收
- 哪些区域需要被垃圾回收机制处理
- 虚拟机栈、程序计数器、本地方法栈的大小确定,并且生命周期都随线程产生和销毁,栈中的栈帧也会随着方法的调用和退出而压入和弹出
- 堆和方法区中的内存空间是在运行时进行分配的,具有不确定性,大小和数目是不确定的,需要进行销毁,这里的Java虚拟机使用GC回收机制对堆中和方法区的对象进行回收和处理
- 如何判断对象是否可以回收
- 当一个对象没有引用指向之后,就表示没有程序会访问它了,这时对象可以被回收
- 引用计数法,为每一个对象设置一个引用计数器,当由引用指向时,计数器就+1,当失去一个指向时,计数器就-1,当一个对象的引用计数器为0时,表示对象可以回收了。这个方法虽然高效和实现简单,但是无法处理一些循环引用的问题
- 可达性分析法,可以通过一些GC ROOTS对象,向下搜索,搜索过形成一个引用链,当一个对象和所有的GC ROOTS对象之间无法形成引用链时,JVM就会将对象进行第一次标记;然后会进行第二次的小规模标记,如果对象没有重写finalize()方法,或者JVM已经执行过这个方法,就会对对象进行直接回收,如果对象重写了finalize()方法,并且JVM没有执行这个方法,JVM就会将对象加入F-QUEUE中,产生一个低优先级的线程除法对象的finalize方法,如果方法执行之后,对象没有和引用链中的任何对象产生链接的话,就会被回收
- GC ROOTS对象有哪些
- 虚拟机栈中的对象的引用,本地方法栈中的对象的引用
- 方法区中类静态变量的对象引用,方法区中常量的对象引用
- 方法区是否可以被回收
- 方法区中主要被回收的内容有被废弃的常量,和没有被使用的类
- 对于类来说满足下面条件可以被回收
- 所有的实例被回收,包括类的实例和子类的实例
- 类的加载器被回收
- 类的Javalang.Class没有被任何地方引用
- GC算法有哪些
- 标记-清除法,标记出需要回收的对象,然后直接回收空间,缺点是会产生较多的碎片空间
- 复制法:将内存空间一分为二,每一次只是用一部分,将存活的数据复制到另一个部分,然后清除掉原来的部分,缺点是空间利用率降低
- 标记整理法:将存活没有被标记的数据移动到内存中的一端,然后将端边界之外的数据清除
- 分代回收法:通过GC分代年龄将对象分为新生代和老年代,使用不同的回收算法,对于新生代处理可能会回收大量的空间,因此使用复制法,将空间分为一个较大的Eden和两个较小的survivor(8:1:1),每次使用时都会使用Eden和一个Survivor空间,回收时将未标记的对象移动到另一个Servivor空间,然后清除掉Eden和Servivor的数据;对于老年代每次回收智慧回收一小部分数据,可以使用标记-清楚法进行回收
- 什么时三色标记
- 白色表示对象没有被访问,初始状态时所有的对象都为白色
- 灰色表示对象被访问,但是对象的引用还没有被访问完全,将GC ROOTs引用的对象标记为灰色
- 黑色表示自己被访问并且自己的引用对象也被访问,灰色节点进行向下搜索,搜索玩所有的引用对象后就标记为黑色,最后标记为白色的就是可回收的对象
- 什么时多标和漏标
- 当内存回收机制需要和用户线程并发时,会产生多表和漏标问题
- 多标是指,当被标记存活的对象不被原来的引用引用了,会产生浮动垃圾,需要等待下一次GC才能回收
- 漏标是指,对于已经标记为黑的对象增加了新的引用对象,就会漏标这个新的引用对象,可以使用读屏障来记录增加的对象
- 当内存回收机制需要和用户线程并发时,会产生多表和漏标问题