1.问题和思路
在最近的项目中遇到一个非常棘手的问题,系统在使用一段时间之后,就会直接down掉,经过排查error日志,发现了内存溢出的报错,报错如下:
分享一切喜欢的事物
在未接触kafka实际的场景时,我一直以为他只是一个
2019年7月31日,我终于踏上了飞往深圳的航班,虽然今天深圳的天气由于台风的影响晚点了将近4个小时,但一点也没有打消掉我对去深圳的那份激动的心情。至于为什么会这么想去深圳呢? 可能是因为那里有一群待宰的小肥羊(如上图所示,一群一直喊着只要我去深圳就要带我吃遍深圳美食的大学同学)。哈哈哈,小肥羊,你们的狼叔叔就要来了(哇哦……)
Spring Boot是现在实现微服务最基础,最受欢迎的一个Spring开发框架,其本身拥有Spring的全部功能,并且提供了很多特性和功能来简化开发人员的开发,深受喜欢,我也是其忠实粉丝之一。
如果你问我你为什么会喜欢使用这个框架,我不会告诉你它有如下的优势,我会喜欢看着你写着一大堆的XML配置,哈哈哈……。
线程池在日常的开发中占据着非常的重要作用,即便我们在日常的开发中没有使用过线程池,也肯定听过周边的开发人员提到过过线程池这个东西。那么,线程池到底是用来做什么?为了解决什么问题呢?
在之前的博客(Synchronized和lock详解)中提到过,线程是最小的运算逻辑单元,合理的使用线程可以充分的利用资源,提升运行的效率,但是使用大量的线程也有相应的缺点。
为了防止出现上述问题,国内著名公司阿里巴巴的编程规范就要求,对于使用到线程创建的都应该使用到线程池,而且应使用ThreadPoolExecutor来进行线程池的创建,至于为什么需要使用ThreadPoolExecutor来创建,这个我们之后在详细说明。阿里巴巴公司的编码规范要求必须使用线程池创建和管理线程,足以说明了线程池的重要性,所以使用和掌握线程池很重要。接下来,我会从总体到细致的方式,来共同学习线程池。
线程是最小的运算逻辑单元,合理的使用线程可以充分利用CPU资源,线程之间采用堆内存的方式进行交互,多个线程之间共享堆内存,但这会存在一个问题,如果不合理的使用线程,就会导致代码执行的安全性和准确性。那么Java是如何解决呢?
什么会造成线程死锁?如何解决?
造成原因:
红黑树是什么?请说明下特点和实现原理?
什么是TCP协议的三次握手,做了什么?为什么不能使用两次握手协议?
答:TCP协议是TCP可靠性实现的基础和保证。
如何实现多个线程满足某个条件时继续往下执行?CyclicBarrier和CountdownLatch的区别是什么?
答:实现多个线程满足条件继续执行可以采用java.util.councurrent包下的CyclicBarrier和CountdownLatch类。
JVM启动参数你都知道哪些?说明下是干什么用的?
答:JVM启动参数分之为三类:标准参数、非标准参数和非Stable参数
标准参数:
-verbose:class 输出JVM载入类的相关信息,当报异常
-verbose:gc 输出每次GC的相关信息
-verbose:jni 输出native方法调用的相关情况,一般用于诊断jni调用的错误信息
非标准参数:
-Xms256M 设置堆内存的大小
-Xmx1024M 设置堆的最大内存的大小
-Xms128M 设置新生代的大小
-Xss1M 设置每个线程的堆栈大小
非Stable参数:
命令及参数 | 描述 |
---|---|
-XX:-DisableExplicitGC | 禁用System.gc() |
-XX:-UseConcMarkSweepGC | 对老年代采用并发标记算法进行GC |
-XX:-UseParallelGC | 启用并行GC |
-XX:-UseSerialGC | 启用串行GC |
-XX:MaxNewSize=size | 设置新生代内存的最大值 |
-XX:MaxPermSize=size | 设置永久代占用内存的最大值 |
-XX:NewRation=2 | 设置新生代和老年代的比例 |
-XX:ErrorFile=XXX.log | 保存错误日志到文件中 |
-XX:HeapDumpPath=XXX.hprof | 指定导出堆信息时的路径 |
-XX:-HeapDumpOutOfMemoryError | 指定导出堆信息时的路径 |
-XX:-PrintGC | 输出GC的相关信息 |
-XX:-PrintDC Details | 输出GC的详细信息 |
-XX:-PrintGCTimeStamps | 输出每次GC的时间戳 |
Cookie、Session、Token的区别是什么?
Java内存泄露造成的原因是什么?怎么排查与解决?
答:内存泄露造成的原因包含两个条件:泄露对象到GC Root拥有有向图,从而造成虚拟机GC时不能回收对象;二是该对象是无用对象。
排查:
缓存穿透、缓存雪崩、缓存击穿造成的原因是什么?怎么解决?
答:
- 对于用户访问不存在的键值将访问数据的数据也保存到缓存中,设置缓存失效的时间比较短即可;
- 使用布隆过滤器,过滤用户请求的键值数据
- 在设置缓存的键值失效时间时,对设置的时间再加上一个随机的时间,从而使键值不会在某一刻全部失效
- 对于热点数据,不设置失效时间,即永久存在
- 若是缓存数据库是分布式部署,将热点数据均匀的分布在不同的缓存数据库中
- 设置互斥锁
- 设置热点数据不过期
redis怎么实现高可用?
答: redis实现高可用,可以采用集群部署以及哨兵模式。
redis自身提供了集群部署的功能,采用redis-strib来实现集群部署。redis集群部署采用的一致性hash的数据结构,将redis需要保存数据的分为16384个槽,然后将些槽分布到集群中的redis节点,如果一个数据需要保存到redis时,即可通过获取键的hash值,算出键应该保存在哪个槽,从而计算保存在redis的哪个节点上。这样有个不好的地方是,如果需要后期扩展的时候,我们遵循一致性hash算法的特点,将顺时针临近的一个节点需要重新计算键的保存节点。所以最好在前期设计的时候就将redis的节点数量设置好。
redis哨兵模式主要解决什么问题以及实现的原理?
答:哨兵模式主要有两个功能,其一是监控各个redis节点是否正常工作;其二是当哨兵认为redis数据库处于客观下线的时候会将从数据库升级为主数据库。
实现原理:
redis复制的实现原理?
答:
redis对于内部不足时都有哪些策略淘汰键?
答:
redis对于键过期的执行策略有哪些?
答:
MyBatis中$和#符号的区别
Java虚拟机在什么情况下会触发Full GC(老年代GC)?
Java生产问题排查步骤?
答:针对不同的情况查问题也不同
Java中ArrayList、LinkedList、Vector的区别?
Spring中BeanFactory和ApplicationContext的区别?
答:BeanFactory和ApplicationContext都是Spring容器。BeanFactory是Spring最底层的接口,只提供了getBean的相关方法。ApplicationContext继承BeanFactory,但提供了更多的功能。
Spring中IOC你是怎么理解的?
答:在未使用Spring IOC开发时,对象的实例化一般由开发人员自己实例化和管理,使用new关键字来实例化。而Spring IOC是将对象的实例化和周期管理交给了BeanFactory去处理,开发人员不需要管理对象的创建和销毁,只需要关注自己的业务处理即可。
Spring中的事务传播机制有哪些?
答:
Spring的事务隔离级别有哪些?
答:
Spring AOP理解?
答:
Spring AOP都有哪些增强?
Spring AOP都有哪几种实现,区别是什么?
答:
Spring AOP的实现原理是什么?
答:
Spring Boot都有哪些特性?
答:
Spring Boot是怎么实现自动配置的?
并发编程
1.多处理器,充分利用系统资源
2.建模的简单性
3.异步处理,能够灵敏响应
1.竟态性条件:多处理器操作在某个时间段,操作同一个地址,造成数据结果不正确
2.活跃性问题:多线程同步快之间发生相互调用,造成死锁,从而导致后续代码不执行,系统崩溃
3.性能问题:如果线程数量过大,并且线程切换比较频繁,则会造成CPU的使用消耗在上线文切换等操作,造成性能问题
##安全性问题解决
1.保证一个对象是无状态的:即保证一个类中的类变量和实例变量是无状态的。
2.操作是原子操作:对于基本的类型的,JAVA提供了原子变量类来实现基本类型的原子操作,其余的复合操作可以使用锁或者同步块,保证原子操作。
3.锁:锁是保证原子操作的一个重要的实现机制。
- void lock():获取锁,调用该方法当前线程会获取锁
- void lockInterruptibly(): 可中断的获取锁,在锁的获取中可以中断当前线程
- boolean tryLock(): 尝试非阻塞的获取锁,调用该方法后立刻返回,如果能获取到则返回true,否则返回false
- boolean tryLock(long time, TimeUnit unit):查实的获取锁
- void unlock(): 释放锁
- Condition newCondition():获取等待通知的组件,该组件和当前的锁绑定,当前线程获取锁然后调用组件的wait()方法,调用后释放锁。
注意:Lock的主要实现依赖于AbstractQueueSynchronizer,一般在Lock的内部创建静态的队列同步器类
- getState(): 获取当同步器的状态
- setState(): 设置当前同步器的状态
- compareAndSetState(): 对比同步器的状态并设置新的状态,这个是原子操作
- 同步块和ReenTryLock都隐式的使用了重入锁,即当前拥有锁的线程和想要获取锁的线程是同一个线程,则同步器的状态想要的增加,释放时也要多次释放。
- 概述: 读写锁满足同一时刻允许多个线程进行读取,但是在写线程访问时,所有的读线程和写线程均阻塞。
- 特性:
a. 公平性选择
b. 支持重进入
c. 支持锁降级- ReentrantReadWriteLock的接口和示例:
a. readLock: 获取读锁
b. wirteLock: 获取写锁
c. int getReadLockCount(): 返回当前读锁被获取的次数
d. int getReadHoldCount(): 返回当前线程获取读锁的次数
e. boolean isWriteLocked(): 判断写锁是否被获取
f. int getWrieteHoldCount():返回当前写锁被获取的次数。- 读写锁的实现:也采用的是队列同步器的实现,需要通过个整型变量来维护多个读线程和一个写线程的状态,采用的是高16位代表读,低16位代表写。
- 概述:LockSupport工具提供了基本的线程阻塞和线程唤醒功能。
a. void park(): 阻塞当前线程,直到调用unPark(Thread thread)方法或者当前线程被中断才返回。
b. void parkNanos(long nanos): 阻塞当前线程最长不超过nanos纳秒。
c. void parkUntil(long deadline): 阻塞当前线程
d. void unPark(Thread thread): 唤醒处于阻塞状态的线程。
- 概述:Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式(Condition condition =Lock.newCondition();
注意:Condition接口具有多个队列,并且可以进入等待队列的线程可以不响应中断。
a. void await(): 当前线程进入等待状态直到被中断或被通知。
b.void awaitUninterruptibly(): 当前线程进入等待状态直到被通知。
c. long awaitNanos(long nanos): 当前线程进入等待状态直到被通知、中断和超时。
d. boolean awaitUtil(Date dealine): 当前线程进入等待状态直到被中断、通知和直到某个时间。
e. void singal(): 唤醒condition队列等待的状态。
f. void singalAll(): 唤醒Condition队列上所有等待的状态。