java内存区域, 各个模块的作用
线程共享:堆,方法区,直接内存
线程私有的:程序计数器,虚拟机栈,本地方法栈
程序计数器:记录线程执行的位置行数,为了线程切换后能恢复正确的执行位置
虚拟机栈:存储局部变量表,常量池的引用,方法的出口等,一个方法的执行意味着一个栈帧入栈出栈的过程
本地方法栈:与栈类似,它用的是虚拟机的native方法
为保证线程中局部变量不被其他线程访问到,所以虚拟机栈和本地方法栈是线程私有的
堆:是java虚拟机管理内存最大的一块,存储实例对象和数组,是垃圾回收管理主要区域
方法区:存储类信息,常量和静态变量,接口,变量,方法名等描述信息
垃圾回收算法
1.引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。
不能解决项目引用的问题。
2.可达性分析法
利用JVM对象引用图,从根节点遍历对象应用图,同时标记遍历到的对象。遍历结束后未被标记的对象就是不在使用的对象了
可作为GC Roots的对象包括下面几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象。
方法区中类静态属性引用的对象。
方法区中常量引用的对象。
本地方法栈中JNI(即一般说的Native方法)引用的对象。
1.标记-清除算法
算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
它是最基础的收集算法,效率也很高,但是会带来两个明显的问题: 效率问题 空间问题(标记清除后会产生大量不连续的碎片)
2.复制算法
它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
3.标记-整理算法
根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
4.分代收集算法
根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
堆溢出,栈溢出举例, 内存溢出与泄漏的区别并举例
堆OutOfMemoryError(Java heap space):堆中主要存储的是对象。如果不断的new对象则会导致堆中的空间溢出 -Xms 去调整堆的大小
栈StackOverflowError:创建的栈帧超过了栈的深度,**死循环或递归调用**,-Xss 去调整栈的大小
内存泄露memory leak:1.在堆中申请的空间没有被释放,2。对象已经不在使用还在内存中保留 -Xms10M -Xmx10M控制
原因:1.静态集合类 2.各种连接不显示的close 3.监听器没有删除 4.变量不合理的作用域
内存溢出OutOfMemory:新建对象,对象所需要的内存大于堆剩余空间 -调大-Xmx
原因:代码中存在死循环或循环产生过多重复的对象实体
内存中加载的数据量过于庞大,如一次从数据库取出过多数据
双亲委派模型, java类加载过程,每个过程做了什么
加载->验证->准备->解析->初始化
加载:找到class文件导入
验证:验证class文件正确性
准备:为类变量(静态变量)分配内存和设置初始值,(在方法区分配内存)`public static int value=123;`
解析:给符号引用转变为直接引用
初始化:对静态变量和静态代码块执行初始化工作
双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。只有当父加载器在自己的搜索范围内找不到指定的类时,子加载器才会尝试自己去加载。
启动类加载器(Bootstrap ClassLoader)->扩展类加载器(Extension ClassLoader)->应用程序类加载器(Application ClassLoader)->自定义classLoader
java中的锁, 乐观锁,悲观锁, 自旋锁等等
乐观锁:读取数据时不加锁,在更新操作的时候才对冲突检测
悲观锁:操作数据的时候就上锁,所以整个处理过程数据都是被锁住的,synchronized就是悲观锁
自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
可重入锁:又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。 ReentrantLock Synchronized
独享锁:是指该锁一次只能被一个线程所持有。 ReentrantLock Synchronized
共享锁:是指该锁可被多个线程所持有。 ReadWriteLock,其读锁是共享锁,其写锁是独享锁
分段锁:其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作
https://www.cnblogs.com/lxmyhappy/p/7380073.html
ReentrantLock与synchronized
都是可重入锁
ReentrantLock是JDK实现的,synchronized是基于JVM实现的
synchronized由编译器加锁和释放锁,ReentrantLock需要手动
ReentrantLock是公平锁,就是先等待的线程可以先获得锁
ReentrantLock可以分组唤醒线程
ReentrantLock提供中断等待锁的机制lock.lockInterruptibly()
volatile的作用, CAS的原理, 在java中哪些地方有用到
volatile:保证线程可见性,一个线程修改了变量值,对其他线程是立即可见的
CAS(比较并交换) 解决原子性;操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。
如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。
CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”
https://blog.csdn.net/v123411739/article/details/79561458
线程同步的方式
https://www.cnblogs.com/XHJT/p/3897440.html
volatile synchronized ReentrantLock
ConcurrentHashMap的原理,是如何保证多线程安全的
https://www.cnblogs.com/ITtangtang/p/3948786.html
多线程的应用,理解
多个线程同时运行,减少线程上下文切换的时间
利用多线程可以提高系统整体并发能力及性能
Java的内存模式:是从主存读取变量,线程吧变量保存在本地内存(寄存器)中,不是直接从主存进行读写,
一个线程修改主存的变量值,另一起还在使用寄存器中拷贝的值,造成数据不一致
volatile变量–多线程间可见 (每次都从主存进行读取)
synchronized-一时刻只能有一个线程能获取到锁
原子性:Atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
同步异步:
举例同步,你喊我吃饭,如果听见了,就一起去吃饭,如果没听见,你就不停喊,知道我听见才一起去吃饭,
异步,你喊我吃饭,然后自己去吃饭,我听到消息可能立刻走也可能等下班才去吃饭。
LRU原理, LinkedHashMap是如何实现的, LinkedHashMap数据结构源码
死锁
https://blog.csdn.net/ls5718/article/details/51896159
互斥条件,不剥夺条件,请求等待条件,循环等待
索引
索引就是个目录,字典的目录,有了目录就能更快的定位
为了方便我们查找,提高查询的效率。
缺点:索引需要维护成本,索引文件是单独存在的,数据的增删改 会产生会索引的额外操作,可能会影响增删改的速度
原理:没有索引就是遍历整张表去查找
把无序的数据变成有序的查询,把随机变成顺序
1、把创建了索引的列的内容进行排序
2、对排序结果生成倒排表
3、在倒排表内容上拼上数据地址链
4、在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
使用树形索引,还有哈希索引它适合单条查询
联合索引查询优化, 什么情况会失效, a,b,c分别建索引失效情况
mysql存储引擎的对比, 为什么用B+树实现
事物的隔离级别,事物的实际应用
1.脏读:指在一个事务处理过程里读取了另一个未提交的事务中的数据。
2.不可重复读:一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
3.幻读:同样的事务操作,在前后两个时间段内执行对同一个数据项的读取,可能出现不一致的结果。幻读和不可重复读都是读取了另一条已经提交的事务
@Transactional(value = "transactionManager", propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
隔离级别是指若干个并发的事务之间的隔离程度
DEFAULT :默认的,
READ_UNCOMMITTED:读未提交,这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用
READ_COMMITED:读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读
REPEATABLE_READ:重复读取,明确数据读取出来就是为了更新用的,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决
SERLALIZABLE:串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题
HAVING 用法,join用法
WHERE 子句用来筛选 FROM 子句中指定的操作所产生的行。
GROUP BY 子句用来分组 WHERE 子句的输出。
HAVING 子句用来从分组的结果中筛选行。 HAVING 语法与 WHERE 语法类似,但 HAVING 可以包含聚合函数。
JOIN(inner join) 只有两个表格都满足条件,才会列出
LEFT JOIN 关键字会从左表那里返回所有的行,即使在右表中没有匹配的行。(RIGHT JOIN同理)
TCP可靠传输
确认应答(三次四次),超时重传,流量控制,拥塞控制
校验和:TCP将保持它首部和数据的检验和。
###### 解决幂等性问题
幂等性:其任意多次执行对资源本身所产生的影响均与一次执行的影响相同,比如支付,下单。
处理方式:每个请求有唯一标识,订单支付请求,订单id
处理完请求后,用一个纪录标识这个请求处理过了,常见方案是在数据库中记录状态
每次接受请求进行判断,比如订单已支付,数据库存在数据就不在处理了
redis请求加锁 乐观悲观锁
http请求header都包含什么?
content-type、accept、host、user-agent
5XX错误码
500 Internal Server Error:
502 Bad Gateway:
504 Gateway Timeout:
跨域问题解决
1 | String origin = request.getHeader("origin"); |
###### 表单的重复提交
在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
###### 过滤器拦截器应用,区别
filter->servlet->intercept->controller
过滤器:Filter是实现了javax.servlet.Filter接口的服务器端程序
过滤器用途:设置字符集,控制权限,过滤掉非法url,
拦截器: SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的
拦截器用途:权限检查,如登录检查
创建一个Filter只需两个步骤
创建Filter处理类
web.xml文件中配置Filter
当web应用重新启动或销毁时,Filter也被销毁
void init(FilterConfig config):用于完成Filter的初始化。
void destory():用于Filter销毁前,完成某些资源的回收。
void doFilter(ServletRequest request,ServletResponse response,FilterChain chain):实现过滤功能
拦截器实现
preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,该方法将在请求处理之前进行调用。当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;
postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,就是在当前请求进行处理之后,也就是Controller 方法调用之后执行
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
过滤器和拦截器的区别:
①拦截器是基于java的反射机制的,而过滤器是基于函数回调。
②拦截器不依赖servlet容器,过滤器依赖servlet容器。
③拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
④拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
⑤在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
⑥拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
原文:https://blog.csdn.net/chenleixing/article/details/44573495