diff --git a/JVM/JVM.md b/JVM/JVM.md index ddccaf8..e6ef355 100644 --- a/JVM/JVM.md +++ b/JVM/JVM.md @@ -1,3 +1,4 @@ +# JVM 常考面试题 ## 1. 什么是JVM内存结构? ![](http://blog-img.coolsen.cn/img/image-20210220111553294.png) @@ -367,7 +368,7 @@ Full GC: 收集整个堆,包括 新生代,老年代,永久代(在 JDK 1.8 - JDK 9,Extension ClassLoader 被 Platform ClassLoader 取代,当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载。打破的原因,是为了添加模块化的特性。 -## 21.说一下 JVM 调优的命令? +## 21. 说一下 JVM 调优的命令? * jps:JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。 * jstat:jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 @@ -375,21 +376,409 @@ Full GC: 收集整个堆,包括 新生代,老年代,永久代(在 JDK 1.8 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。 * jhat:jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。 * jstack:jstack用于生成java虚拟机当前时刻的线程快照。jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。 -## Java对象创建过程 +## 22. Java对象创建过程 1. JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲) 2. 为对象分配内存。一种办法"指针碰撞"、一种办法"空闲列表",最终常用的办法"本地线程缓冲分配(TLAB)" 3. 将除对象头外的对象内存空间初始化为0 4. 对对象头进行必要设置 -## 巨人的肩膀 +## 23. JDK新特性 -https://jishuin.proginn.com/p/763bfbd35094 +**JDK8** -https://www.javanav.com/val/93550f179edb4a77bbf4d35faa6d560c.html +支持 Lamda 表达式、集合的 stream 操作、提升HashMap性能 -https://juejin.cn/post/6844903941805703181 +**JDK9** -https://www.cnblogs.com/chiangchou/p/jvm-2.html +```java +//Stream API中iterate方法的新重载方法,可以指定什么时候结束迭代 +IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println); +``` -https://juejin.cn/post/6844903887866953735 +默认G1垃圾回收器 + +**JDK10** + +其重点在于通过完全GC并行来改善G1最坏情况的等待时间。 + +**JDK11** + +ZGC (并发回收的策略) 4TB + +用于 Lambda 参数的局部变量语法 + +**JDK12** + +Shenandoah GC (GC 算法)停顿时间和堆的大小没有任何关系,并行关注停顿响应时间。 + +**JDK13** + +增加ZGC以将未使用的堆内存返回给操作系统,16TB + +**JDK14** + +删除cms垃圾回收器、弃用ParallelScavenge+SerialOldGC垃圾回收算法组合 + +将ZGC垃圾回收器应用到macOS和windows平台 + + + + + +# 线上故障排查 + +## 1、硬件故障排查 + +如果一个实例发生了问题,根据情况选择,要不要着急去重启。如果出现的CPU、内存飙高或者日志里出现了OOM异常 + +**第一步是隔离**,第二步是**保留现场**,第三步才是**问题排查**。 + +**隔离** + +就是把你的这台机器从请求列表里摘除,比如把 nginx 相关的权重设成零。 + +**现场保留** + +**瞬时态和历史态** + +![img](https://tva1.sinaimg.cn/large/008eGmZEly1gobnwy22d2j30l10cpt9d.jpg) + +查看比如 CPU、系统内存等,通过历史状态可以体现一个趋势性问题,而这些信息的获取一般依靠监控系统的协作。 + +**保留信息** + +(1)**系统当前网络连接** + +``` +ss -antp> $DUMP_DIR/ss.dump 2>&1 +``` + + +使用 ss 命令而不是 netstat 的原因,是因为 netstat 在网络连接非常多的情况下,执行非常缓慢。 + +后续的处理,可通过查看各种网络连接状态的梳理,来排查 TIME_WAIT 或者 CLOSE_WAIT,或者其他连接过高的问题,非常有用。 + +(2)**网络状态统计** + +```java +netstat -s> $DUMP_DIR/netstat-s.dump 2>&1 +``` + + +它能够按照各个协议进行统计输出,对把握当时整个网络状态,有非常大的作用。 + +```java +sar -n DEV 1 2> $DUMP_DIR/sar-traffic.dump 2>&1 +``` + + +在一些速度非常高的模块上,比如 Redis、Kafka,就经常发生跑满网卡的情况。表现形式就是网络通信非常缓慢。 + +(3)**进程资源** + +```java +lsof -p $PID> $DUMP_DIR/lsof-$PID.dump +``` + + +通过查看进程,能看到打开了哪些文件,可以以进程的维度来查看整个资源的使用情况,包括每条网络连接、每个打开的文件句柄。同时,也可以很容易的看到连接到了哪些服务器、使用了哪些资源。这个命令在资源非常多的情况下,输出稍慢,请耐心等待。 + +(4)**CPU 资源** + +``` +mpstat> $DUMP_DIR/mpstat.dump 2>&1 +vmstat 1 3> $DUMP_DIR/vmstat.dump 2>&1 +sar -p ALL> $DUMP_DIR/sar-cpu.dump 2>&1 +uptime> $DUMP_DIR/uptime.dump 2>&1 +``` + +主要用于输出当前系统的 CPU 和负载,便于事后排查。 + +(5)**I/O 资源** + +```java +iostat -x> $DUMP_DIR/iostat.dump 2>&1 +``` + + +一般,以计算为主的服务节点,I/O 资源会比较正常,但有时也会发生问题,比如**日志输出过多,或者磁盘问题**等。此命令可以输出每块磁盘的基本性能信息,用来排查 I/O 问题。在第 8 课时介绍的 GC 日志分磁盘问题,就可以使用这个命令去发现。 + +(6)**内存问题** + +```java +free -h> $DUMP_DIR/free.dump 2>&1 +``` + + +free 命令能够大体展现操作系统的内存概况,这是故障排查中一个非常重要的点,比如 SWAP 影响了 GC,SLAB 区挤占了 JVM 的内存。 + +(7)**其他全局** + +```java +ps -ef> $DUMP_DIR/ps.dump 2>&1 +dmesg> $DUMP_DIR/dmesg.dump 2>&1 +sysctl -a> $DUMP_DIR/sysctl.dump 2>&1 +``` + + +dmesg 是许多静悄悄死掉的服务留下的最后一点线索。当然,ps 作为执行频率最高的一个命令,由于内核的配置参数,会对系统和 JVM 产生影响,所以我们也输出了一份。 + +(8)**进程快照**,最后的遗言(jinfo) + +```java +${JDK_BIN}jinfo $PID> $DUMP_DIR/jinfo.dump 2>&1 +``` + + +此命令将输出 Java 的基本进程信息,包括**环境变量和参数配置**,可以查看是否因为一些错误的配置造成了 JVM 问题。 + +**(9)dump 堆信息** + +```java +${JDK_BIN}jstat -gcutil $PID> $DUMP_DIR/jstat-gcutil.dump 2>&1 +${JDK_BIN}jstat -gccapacity $PID> $DUMP_DIR/jstat-gccapacity.dump 2>&1 +``` + + +jstat 将输出当前的 gc 信息。一般,基本能大体看出一个端倪,如果不能,可将借助 jmap 来进行分析。 + +**(10)堆信息** + +```java +${JDK_BIN}jmap $PID> $DUMP_DIR/jmap.dump 2>&1 +${JDK_BIN}jmap -heap $PID> $DUMP_DIR/jmap-heap.dump 2>&1 +${JDK_BIN}jmap -histo $PID> $DUMP_DIR/jmap-histo.dump 2>&1 +${JDK_BIN}jmap -dump:format=b,file=$DUMP_DIR/heap.bin $PID> /dev/null 2>&1 +``` + + +jmap 将会得到当前 Java 进程的 dump 信息。如上所示,其实最有用的就是第 4 个命令,但是前面三个能够让你初步对系统概况进行大体判断。因为,第 4 个命令产生的文件,一般都非常的大。而且,需要下载下来,导入 MAT 这样的工具进行深入分析,才能获取结果。这是分析内存泄漏一个必经的过程。 + +**(11)JVM 执行栈** + +```java +${JDK_BIN}jstack $PID> $DUMP_DIR/jstack.dump 2>&1 +``` + + +jstack 将会获取当时的执行栈。一般会多次取值,我们这里取一次即可。这些信息非常有用,能够还原 Java 进程中的线程情况。 + +```java +top -Hp $PID -b -n 1 -c> $DUMP_DIR/top-$PID.dump 2>&1 +``` + + +为了能够得到更加精细的信息,我们使用 top 命令,来获取进程中所有线程的 CPU 信息,这样,就可以看到资源到底耗费在什么地方了。 + +**(12)高级替补** + +```java +kill -3 $PID +``` + + +有时候,jstack 并不能够运行,有很多原因,比如 Java 进程几乎不响应了等之类的情况。我们会尝试向进程发送 kill -3 信号,这个信号将会打印 jstack 的 trace 信息到日志文件中,是 jstack 的一个替补方案。 + +```java +gcore -o $DUMP_DIR/core $PID +``` + + +对于 jmap 无法执行的问题,也有替补,那就是 GDB 组件中的 gcore,将会生成一个 core 文件。我们可以使用如下的命令去生成 dump: + +```java +${JDK_BIN}jhsdb jmap --exe ${JDK}java --core $DUMP_DIR/core --binaryheap +``` + +3. **内存泄漏的现象** + +稍微提一下 jmap 命令,它在 9 版本里被干掉了,取而代之的是 jhsdb,你可以像下面的命令一样使用。 + +```java +jhsdb jmap --heap --pid 37340 +jhsdb jmap --pid 37288 +jhsdb jmap --histo --pid 37340 +jhsdb jmap --binaryheap --pid 37340 +``` + +一般内存溢出,表现形式就是 Old 区的占用持续上升,即使经过了多轮 GC 也没有明显改善。比如ThreadLocal里面的GC Roots,内存泄漏的根本就是,这些对象并没有切断和 GC Roots 的关系,可通过一些工具,能够看到它们的联系。 + + + +## 2、报表异常 | JVM调优 + +有一个报表系统,频繁发生内存溢出,在高峰期间使用时,还会频繁的发生拒绝服务,由于大多数使用者是管理员角色,所以很快就反馈到研发这里。 + +业务场景是由于有些结果集的字段不是太全,因此需要对结果集合进行循环,并通过 HttpClient 调用其他服务的接口进行数据填充。使用 Guava 做了 JVM 内缓存,但是响应时间依然很长。 + +初步排查,JVM 的资源太少。接口 A 每次进行报表计算时,都要涉及几百兆的内存,而且在内存里驻留很长时间,有些计算又非常耗 CPU,特别的"吃"资源。而我们分配给 JVM 的内存只有 3 GB,在多人访问这些接口的时候,内存就不够用了,进而发生了 OOM。在这种情况下,没办法,只有升级机器。把机器配置升级到 4C8G,给 JVM 分配 6GB 的内存,这样 OOM 问题就消失了。但随之而来的是频繁的 GC 问题和超长的 GC 时间,平均 GC 时间竟然有 5 秒多。 + +进一步,由于报表系统和高并发系统不太一样,它的对象,存活时长大得多,并不能仅仅通过增加年轻代来解决;而且,如果增加了年轻代,那么必然减少了老年代的大小,由于 CMS 的碎片和浮动垃圾问题,我们可用的空间就更少了。虽然服务能够满足目前的需求,但还有一些不太确定的风险。 + +第一,了解到程序中有很多缓存数据和静态统计数据,为了减少 MinorGC 的次数,通过分析 GC 日志打印的对象年龄分布,把 MaxTenuringThreshold 参数调整到了 3(特殊场景特殊的配置)。这个参数是让年轻代的这些对象,赶紧回到老年代去,不要老呆在年轻代里。 + +第二,我们的 GC 时间比较长,就一块开了参数 CMSScavengeBeforeRemark,使得在 CMS remark 前,先执行一次 Minor GC 将新生代清掉。同时配合上个参数,其效果还是比较好的,一方面,对象很快晋升到了老年代,另一方面,年轻代的对象在这种情况下是有限的,在整个 MajorGC 中占的时间也有限。 + +第三,由于缓存的使用,有大量的弱引用,拿一次长达 10 秒的 GC 来说。我们发现在 GC 日志里,处理 weak refs 的时间较长,达到了 4.5 秒。这里可以加入参数 ParallelRefProcEnabled 来并行处理Reference,以加快处理速度,缩短耗时。 + +优化之后,效果不错,但并不是特别明显。经过评估,针对高峰时期的情况进行调研,我们决定再次提升机器性能,改用 8core16g 的机器。但是,这带来另外一个问题。 + +**高性能的机器带来了非常大的服务吞吐量**,通过 jstat 进行监控,能够看到年轻代的分配速率明显提高,但随之而来的 MinorGC 时长却变的不可控,有时候会超过 1 秒。累积的请求造成了更加严重的后果。 + +这是由于堆空间明显加大造成的回收时间加长。为了获取较小的停顿时间,我们在堆上**改用了 G1 垃圾回收器**,把它的目标设定在 200ms。G1 是一款非常优秀的垃圾收集器,不仅适合堆内存大的应用,同时也简化了调优的工作。通过主要的参数初始和最大堆空间、以及最大容忍的 GC 暂停目标,就能得到不错的性能。修改之后,虽然 GC 更加频繁了一些,但是停顿时间都比较小,应用的运行较为平滑。 + +到目前为止,也只是勉强顶住了已有的业务,但是,这时候领导层面又发力,**要求报表系统可以支持未来两年业务10到100倍的增长**,并保持其可用性,但是这个"千疮百孔"的报表系统,稍微一压测,就宕机,那如何应对十倍百倍的压力呢 ? 硬件即使可以做到动态扩容,但是毕竟也有极限。 + +使用 MAT 分析堆快照,发现很多地方可以通过代码优化,那些占用内存特别多的对象: + +1、select * 全量排查,只允许获取必须的数据 + +2、报表系统中cache实际的命中率并不高,将Guava 的 Cache 引用级别改成弱引用(WeakKeys) + +3、限制报表导入文件大小,同时拆分用户超大范围查询导出请求。 + +每一步操作都使得JVM使用变得更加可用,一系列优化以后,机器相同压测数据性能提升了数倍。 + + + +## 3、大屏异常 | JUC调优 + +有些数据需要使用 HttpClient 来获取进行补全。提供数据的服务提供商有的响应时间可能会很长,也有可能会造成服务整体的阻塞。 + +![img](https://tva1.sinaimg.cn/large/008eGmZEly1gobr4whjzwj30l1058dfx.jpg) + +接口 A 通过 HttpClient 访问服务 2,响应 100ms 后返回;接口 B 访问服务 3,耗时 2 秒。HttpClient 本身是有一个最大连接数限制的,如果服务 3 迟迟不返回,就会造成 HttpClient 的连接数达到上限,**概括来讲,就是同一服务,由于一个耗时非常长的接口,进而引起了整体的服务不可用** + +这个时候,通过 jstack 打印栈信息,会发现大多数竟然阻塞在了接口 A 上,而不是耗时更长的接口 B,这个现象起初十分具有迷惑性,不过经过分析后,我们猜想其实是因为接口 A 的速度比较快,在问题发生点进入了更多的请求,它们全部都阻塞住的同时被打印出来了。 + +为了验证这个问题,我搭建了一个demo 工程,模拟了两个使用同一个 HttpClient 的接口。fast 接口用来访问百度,很快就能返回;slow 接口访问谷歌,由于众所周知的原因,会阻塞直到超时,大约 10 s。 利用ab对两个接口进行压测,同时使用 jstack 工具 dump 堆栈。首先使用 jps 命令找到进程号,然后把结果重定向到文件(可以参考 10271.jstack 文件)。 + +过滤一下 nio 关键字,可以查看 tomcat 相关的线程,足足有 200 个,这和 Spring Boot 默认的 maxThreads 个数不谋而合。更要命的是,有大多数线程,都处于 BLOCKED 状态,说明线程等待资源超时。通过grep fast | wc -l 分析,确实200个中有150个都是blocked的fast的进程。 + +问题找到了,解决方式就顺利成章了。 + +1、fast和slow争抢连接资源,通过线程池限流或者熔断处理 + +2、有时候slow的线程也不是一直slow,所以就得加入监控 + +3、使用带countdownLaunch对线程的执行顺序逻辑进行控制 + + + + + + + + + +## **4、接口延迟 | SWAP调优** + +有一个关于服务的某个实例,经常发生服务卡顿。由于服务的并发量是比较高的,每多停顿 1 秒钟,几万用户的请求就会感到延迟。 + +我们统计、类比了此服务其他实例的 CPU、内存、网络、I/O 资源,区别并不是很大,所以一度怀疑是机器硬件的问题。 + +接下来我们对比了节点的 GC 日志,发现无论是 Minor GC,还是 Major GC,这个节点所花费的时间,都比其他实例长得多。 + +通过仔细观察,我们发现在 GC 发生的时候,vmstat 的 si、so 飙升的非常严重,这和其他实例有着明显的不同。 + +使用 free 命令再次确认,发现 SWAP 分区,使用的比例非常高,引起的具体原因是什么呢? + +更详细的操作系统内存分布,从 /proc/meminfo 文件中可以看到具体的逻辑内存块大小,有多达 40 项的内存信息,这些信息都可以通过遍历 /proc 目录的一些文件获取。我们注意到 slabtop 命令显示的有一些异常,dentry(目录高速缓冲)占用非常高。 + +问题最终定位到是由于某个运维工程师删除日志时,定时执行了一句命令: + +find / | grep "xxx.log" + + +他是想找一个叫做 要被删除 的日志文件,看看在哪台服务器上,结果,这些老服务器由于文件太多,扫描后这些文件信息都缓存到了 slab 区上。而服务器开了 swap,操作系统发现物理内存占满后,并没有立即释放 cache,导致每次 GC 都要和硬盘打一次交道。 + + + +**解决方式就是关闭 SWAP 分区。** + + + +swap 是很多性能场景的万恶之源,建议禁用。在高并发 SWAP 绝对能让你体验到它魔鬼性的一面:进程倒是死不了了,但 GC 时间长的却让人无法忍受。 + +## 5、**内存溢出 | Cache调优** + +> 有一次线上遇到故障,重新启动后,使用 jstat 命令,发现 Old 区一直在增长。我使用 jmap 命令,导出了一份线上堆栈,然后使用 MAT 进行分析,通过对 GC Roots 的分析,发现了一个非常大的 HashMap 对象,这个原本是其他同事做缓存用的,但是做了一个无界缓存,没有设置超时时间或者 LRU 策略,在使用上又没有重写key类对象的hashcode和equals方法,对象无法取出也直接造成了堆内存占用一直上升,后来,将这个缓存改成 guava 的 Cache,并设置了弱引用,故障就消失了。 +> +> 关于文件处理器的应用,在读取或者写入一些文件之后,由于发生了一些异常,**close 方法又没有放在 finally** 块里面,造成了文件句柄的泄漏。由于文件处理十分频繁,产生了严重的内存泄漏问题。 + +内存溢出是一个结果,而**内存泄漏**是一个原因。内存溢出的原因有**内存空间不足、配置错误**等因素。一些错误的编程方式,不再被使用的对象、没有被回收、没有及时切断与 GC Roots 的联系,这就是内存泄漏。 + +举个例子,有团队使用了 HashMap 做缓存,但是并没有设置超时时间或者 LRU 策略,造成了放入 Map 对象的数据越来越多,而产生了内存泄漏。 + +再来看一个经常发生的内存泄漏的例子,也是由于 HashMap 产生的。代码如下,由于没有重写 Key 类的 hashCode 和 equals 方法,造成了放入 HashMap 的所有对象都无法被取出来,它们和外界失联了。所以下面的代码结果是 null。 + +```java +//leak example +import java.util.HashMap; +import java.util.Map; +public class HashMapLeakDemo { + public static class Key { + String title; + public Key(String title) { + this.title = title; + } +} + +public static void main(String[] args) { + Map map = new HashMap(); + map.put(new Key("1"), 1); + map.put(new Key("2"), 2); + map.put(new Key("3"), 2); + Integer integer = map.get(new Key("2")); + System.out.println(integer); + } +} +``` + + +即使提供了 equals 方法和 hashCode 方法,也要非常小心,尽量避免使用自定义的对象作为 Key。 + +再看一个例子,关于文件处理器的应用,在读取或者写入一些文件之后,由于发生了一些异常,**close 方法又没有放在 finally** 块里面,造成了文件句柄的泄漏。由于文件处理十分频繁,产生了严重的内存泄漏问题。 + +## 6、CPU飙高 | 死循环 + +我们有个线上应用,单节点在运行一段时间后,CPU 的使用会飙升,一旦飙升,一般怀疑某个业务逻辑的计算量太大,或者是触发了死循环(比如著名的 HashMap 高并发引起的死循环),但排查到最后其实是 GC 的问题。 + +(1)使用 top 命令,查找到使用 CPU 最多的某个进程,记录它的 pid。使用 Shift + P 快捷键可以按 CPU 的使用率进行排序。 + +```java +top +``` + + +(2)再次使用 top 命令,加 -H 参数,查看某个进程中使用 CPU 最多的某个线程,记录线程的 ID。 + +```java +top -Hp $pid +``` + + +(3)使用 printf 函数,将十进制的 tid 转化成十六进制。 + +```java +printf %x $tid +``` + + +(4)使用 jstack 命令,查看 Java 进程的线程栈。 + +```java +jstack $pid>$pid.log +``` + + +(5)使用 less 命令查看生成的文件,并查找刚才转化的十六进制 tid,找到发生问题的线程上下文。 + +```java +less $pid.log +``` + + +我们在 jstack 日志搜关键字DEAD,以及中找到了 CPU 使用最多的几个线程id。 + +可以看到问题发生的根源,是我们的堆已经满了,但是又没有发生 OOM,于是 GC 进程就一直在那里回收,回收的效果又非常一般,造成 CPU 升高应用假死。接下来的具体问题排查,就需要把内存 dump 一份下来,使用 MAT 等工具分析具体原因了。 -https://segmentfault.com/a/1190000023182342 \ No newline at end of file diff --git a/Netty.md b/Netty.md new file mode 100644 index 0000000..7995495 --- /dev/null +++ b/Netty.md @@ -0,0 +1,31 @@ +1.Netty 是什么? +Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方法灵活。 + +2.Netty 的特点是什么? +高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。 +传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。 +封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。 +3.Netty 的优势有哪些? +使用简单:封装了 NIO 的很多细节,使用更简单。 +功能强大:预置了多种编解码功能,支持多种主流协议。 +定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。 +性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。 +稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。 +社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。 + +4. Netty 的高性能表现在哪些方面? +心跳,对服务端:会定时清除闲置会话 inactive(netty5),对客户端:用来检测会话是否断开,是否重来,检测网络延迟,其中 idleStateHandler 类 用来检测会话状态 + +串行无锁化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。表面上看,串行化设计似乎 CPU 利用率不高,并发程度不够。但是,通过调整 NIO 线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。 + +可靠性,链路有效性检测:链路空闲检测机制,读/写空闲超时机制;内存保护机制:通过内存池重用 ByteBuf;ByteBuf 的解码保护;优雅停机:不再接收新消息、退出前的预处理操作、资源的释放操作。 + +Netty 安全性:支持的安全协议:SSL V2 和 V3,TLS,SSL 单向认证、双向认证和第三方 CA证。 + +高效并发编程的体现:volatile 的大量、正确使用;CAS 和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。IO 通信性能三原则:传输(AIO)、协议(Http)、线程(主从多线程) + +流量整型的作用(变压器):防止由于上下游网元性能不均衡导致下游网元被压垮,业务流中断;防止由于通信模块接受消息过快,后端业务线程处理不及时导致撑死问题。 + +TCP 参数配置:SO_RCVBUF 和 SO_SNDBUF:通常建议值为 128K 或者 256K; + +SO_TCPNODELAY:NAGLE 算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法; diff --git a/README.md b/README.md index 31f7fe7..382b345 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # Java-Interview -「Java面试小抄」一份通向理想互联网公司的面试指南,包括 Java基础、集合、Java并发、JVM、MySQL、Redis、Spring、MyBatis、Kafka、操作系统、计算机网络、系统设计、分布式、Java 项目实战等 +「Java面试小抄」一份通向理想互联网公司的面试指南,包括 Java基础、集合、Java并发、JVM、MySQL、Redis、Spring、MyBatis、Kafka、操作系统、计算机网络、系统设计、分布式、Java 项目实战等。 +> 在线阅读:https://www.javalearn.cn/ +
+


@@ -14,8 +17,11 @@

-
👍推荐免费下载几百本计算机经典 电子书
-
+ + +
👍推荐免费下载几百本计算机经典 电子书 + + ## 更多 @@ -91,6 +97,8 @@ ## Dubbo * [Dubbo](https://github.com/cosen1024/Java-Interview/blob/main/Dubbo/Dubbo%E9%9D%A2%E8%AF%95%E9%A2%98.md) +## 分布式 +* [分布式](/分布式/面试题.md) ## 关于我 -[库森的校招经历](http://mp.weixin.qq.com/s?__biz=Mzg4MjUxMTI4NA==&mid=2247483796&idx=1&sn=bdc95819d442ac946b6e49dee6ee36de&chksm=cf54dd4ff82354594280041ce65f639a8ed3361df03da95bed743474a6075dfa973f35acc029&token=2080698617&lang=zh_CN#rd) +[库森的校招经历](https://mp.weixin.qq.com/s?__biz=MzkyMTI3Mjc2MQ==&tempkey=MTIxNV81aG91ZWFjc0E3SWQzUCtmY2ZRQ3I1QXJ0MjZPcndKU3FVdDhqWVI2dGh0bDBCTE9ZTGxWamhUcmFuODZmRFhubXBQMGtCVHd3UUhPTkRHYVh0cTZJaHhSeUx2aTJkUXJocUtSVVpVUU5qaGZoYUdFVFNPOG15X2tGbWFVM1g5bFVQVlo2SGZmbGVtdjVSU2RVZTlhSW9zT1NtcjFHeG1nNzhQbUlRfn4%3D&chksm=c184814ef6f308589c207f852920ac287d1d5eb7b05846483b6e7699448a8bf2578b7a03bc33&token=483239364&lang=zh_CN#rd) diff --git a/Redis/Redis.md b/Redis/Redis.md index 3ced78a..7ada9b1 100644 --- a/Redis/Redis.md +++ b/Redis/Redis.md @@ -113,7 +113,7 @@ Redis列表结构,LPUSH可以在列表头部插入一个内容ID作为关键 消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。 -## 8. Redis的数据类型有哪些? +## 7. Redis的数据类型有哪些? 有五种常用数据类型:String、Hash、Set、List、SortedSet。以及三种特殊的数据类型:Bitmap、HyperLogLog、Geospatial ,其中HyperLogLog、Bitmap的底层都是 String 数据类型,Geospatial 的底层是 Sorted Set 数据类型。 @@ -143,7 +143,7 @@ Redis列表结构,LPUSH可以在列表头部插入一个内容ID作为关键 # 持久化 -## 6. Redis持久化机制? +## 8. Redis持久化机制? 为了能够重用Redis数据,或者防止系统故障,我们需要将Redis中的数据写入到磁盘空间中,即持久化。 diff --git "a/345円210円206円345円270円203円345円274円217円/351円235円242円350円257円225円351円242円230円.md" "b/345円210円206円345円270円203円345円274円217円/351円235円242円350円257円225円351円242円230円.md" new file mode 100644 index 0000000..fb4e9cc --- /dev/null +++ "b/345円210円206円345円270円203円345円274円217円/351円235円242円350円257円225円351円242円230円.md" @@ -0,0 +1,53 @@ +## 1.解释一下什么是CAP? +* Consistency:一致性就是在客户端任何时候看到各节点的数据都是一致的。 +* Availability:可用性就是在任何时刻都可以提供读写。 +* Partition Tolerance:分区容错性是在网络故障、某些节点不能通信的时候系统仍能继续工作。 +具体地讲在分布式系统中,在任何数据库设计中,一个Web应用最多只能同时支持上面的两个属性。显然,任何横向扩展策略都要依赖于数据分区。因此,设计人员必须在一致性与可用性之间做出选择。 +![img](http://blog-img.coolsen.cn/img/801753-20151107213219867-1667011131.png) + + +AP(高可用&&分区容错): + +允许至少一个节点更新状态会导致数据不一致,即丧失了C性质(一致性)。会导致全局的数据不一致。 + +CP(一致&&分区容错): + +为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质(可用性)。分区同步会导致同步时间无限延长(也就是等数据同步完成之后才能正常访问) + +CA(一致&&高可用): + +两个节点可以互相通信,才能既保证C(一致性)又保证A(可用性),这又会导致丧失P性质(分区容错性)。这样的话就分布式节点受阻,无法部署子节点,放弃了分布式系统的可扩展性。因为分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,节点间的分区故障是必然发生的,所以在分布式系统中分区容错性是必须要考虑的。 +## 2.什么分布式事务? + +分布式事务服务(Distributed Transaction Service,DTS)是一个分布式事务框架,用来保障在大规模分布式环境下事务的最终一致性。 + +CAP理论告诉我们在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的,所以我们只能在一致性和可用性之间进行权衡。 + +为了保障系统的可用性,互联网系统大多将强一致性需求转换成最终一致性的需求,并通过系统执行幂等性的保证,保证数据的最终一致性。 + +## 3.了解BASE理论吗? +BASE理论指的是: + +* Basically Available(基本可用) +* Soft state(软状态) +* Eventually consistent(最终一致性) +BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,是对互联网大规模分布式系统的实践总结,强调可用性。 + +理论的核心思想就是:基本可用(Basically Available)和最终一致性(Eventually consistent)。虽然无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。 + +## 4.实现分布式事务一致性(Consistency)的方法有哪些? +最著名的就是二阶段提交协议、三阶段提交协议和Paxos算法。 + +**两阶段提交协议** + +* prepare(准备阶段) + +当开始事务调用的时候,事务处理器向事务执行者(有可能是数据库本身支持)发出命令,事务执行者进行prepare操作。 +当所有事务执行者都完成了prepare操作,就进行下一步行为。 +如果有一个事务执行者在执行prepare的时候失败了,那么通知事务处理器,事务处理器再通知所有的事务执行者执行回滚操作。 +* commit(提交阶段) + +当所有事务执行者都prepare成功以后,事务处理器会再次发送commit请求给事务执行者,所有事务执行者进行commit处理。 +当所有commit处理都成功了,那么事务执行结束。 +如果有一个事务执行者的commit处理不成功,这个时候就要通知事务处理器,事务处理器通知所有的事务执行者执行回滚(abort)操作。 +但是两阶段提交的诟病就是在于性能问题。比如由于执行链比较长,锁定资源的时间也变长了。所以在高性能的系统中都会避免使用二阶段提交。 \ No newline at end of file diff --git "a/346円223円215円344円275円234円347円263円273円347円273円237円/346円223円215円344円275円234円347円263円273円347円273円237円.md" "b/346円223円215円344円275円234円347円263円273円347円273円237円/346円223円215円344円275円234円347円263円273円347円273円237円.md" index d3395bf..40a79d7 100644 --- "a/346円223円215円344円275円234円347円263円273円347円273円237円/346円223円215円344円275円234円347円263円273円347円273円237円.md" +++ "b/346円223円215円344円275円234円347円263円273円347円273円237円/346円223円215円344円275円234円347円263円273円347円273円237円.md" @@ -1,11 +1,23 @@ -* 一次调用时的状态。 +## 1. 进程和线程的区别? + +* 调度:进程是资源管理的基本单位,线程是程序执行的基本单位。 +* 切换:线程上下文切换比进程上下文切换要快得多。 +* 拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。 +* 系统开销: 创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。 + +## 2. 协程与线程的区别? + +* 线程和进程都是同步机制,而协程是异步机制。 +* 线程是抢占式,而协程是非抢占式的。需要用户释放使用权切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。 +* 一个线程可以有多个协程,一个进程也可以有多个协程。 +* 协程不被操作系统内核管理,而完全是由程序控制。线程是被分割的CPU资源,协程是组织好的代码流程,线程是协程的资源。但协程不会直接使用线程,协程直接利用的是执行器关联任意线程或线程池。 +* 协程能保留上一次调用时的状态。 ## 3. 并发和并行有什么区别? 并发就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。单核处理器可以做到并发。比如有两个进程`A`和`B`,`A`运行一个时间片之后,切换到`B`,`B`运行一个时间片之后又切换到`A`。因为切换速度足够快,所以宏观上表现为在一段时间内能同时运行多个程序。 并行就是在同一时刻,有多个任务在执行。这个需要多核处理器才能完成,在微观上就能同时执行多条指令,不同的程序被放到不同的处理器上运行,这个是物理上的多个进程同时进行。 - ## 4. 进程与线程的切换流程? 进程切换分两步:

AltStyle によって変換されたページ (->オリジナル) /