架构师成长之路:如何保证消息队列的高可用

问题一:描述一下 JVM 的内存区域java

程序计数器(PC,Program Counter Register)。在 JVM 规范中,每一个线程都有它本身的程序计数器,而且任什么时候间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址;或者,若是是在执行本地方法,则是未指定值(undefined)。程序员

Java 虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈。每一个线程在建立时都会建立一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。前面谈程序计数器时,提到了当前方法;同理,在一个时间点,对应的只会有一个活动的栈帧,一般叫做当前帧,方法所在的类叫做当前类。若是在该方法中调用了其余方法,对应的新的栈帧会被建立出来,成为新的当前帧,一直到它返回结果或者执行结束。JVM 直接对 Java 栈的操做只有两个,就是对栈帧的压栈和出栈。栈帧中存储着局部变量表、操做数(operand)栈、动态连接、方法正常退出或者异常退出的定义等。算法

堆(Heap),它是 Java 内存管理的核心区域,用来放置 Java 对象实例,几乎全部建立的Java 对象实例都是被直接分配在堆上。堆被全部的线程共享,在虚拟机启动时,咱们指定的“Xmx”之类参数就是用来指定最大堆空间等指标。理所固然,堆也是垃圾收集器重点照顾的区域,因此堆内空间还会被不同的垃圾收集器进行进一步的细分,最有名的就是新生代、老年代的划分。sql

方法区(Method Area)。这也是全部线程共享的一块内存区域,用于存储所谓的元(Meta)数据,例如类结构信息,以及对应的运行时常量池、字段、方法代码等。因为早期的 Hotspot JVM 实现,不少人习惯于将方法区称为永久代(Permanent Generation)。Oracle JDK 8 中将永久代移除,同时增长了元数据区(Metaspace)。缓存

运行时常量池(Run-Time Constant Pool),这是方法区的一部分。若是仔细分析过反编译的类文件结构,你能看到版本号、字段、方法、超类、接口等各类信息,还有一项信息就是常量池。Java 的常量池能够存放各类常量信息,不管是编译期生成的各类字面量,仍是须要在运行时决定的符号引用,因此它比通常语言的符号表存储的信息更加宽泛。数据结构

本地方法栈(Native Method Stack)。它和 Java 虚拟机栈是很是类似的,支持对本地方法的调用,也是每一个线程都会建立一个。在 Oracle Hotspot JVM 中,本地方法栈和 Java 虚拟机栈是在同一起区域,这彻底取决于技术实现的决定,并未在规范中强制。架构

问题二:形成OOM的缘由有哪几种?并发

堆内存不足是最多见的 OOM 缘由之一,抛出的错误信息是“java.lang.OutOfMemoryError:Java heap space”,缘由可能千奇百怪,例如,可能存在内存泄漏问题;也颇有可能就是堆的大小不合理,好比咱们要处理比较可观的数据量,可是没有显式指定 JVM 堆大小或者指定数值偏小;或者出现 JVM 处理引用不及时,致使堆积起来,内存没法释放等。分布式

虚拟机栈和本地方法栈,这里要稍微复杂一点。若是咱们写一段程序不断的进行递归调用,并且没有退出条件,就会致使不断地进行压栈。相似这种状况,JVM 实际会抛出StackOverFlowError;固然,若是 JVM 试图去扩展栈空间的的时候失败,则会抛出OutOfMemoryError。高并发

对于老版本的 Oracle JDK,由于永久代的大小是有限的,而且 JVM 对永久代垃圾回收(如,常量池回收、卸载不再须要的类型)很是不积极,因此当咱们不断添加新类型的时候,永久代出现OutOfMemoryError 也很是多见,尤为是在运行时存在大量动态类型生成的场合;相似 Intern 字符串缓存占用太多空间,也会致使 OOM 问题。对应的异常信息,会标记出来和永久代相关:“java.lang.OutOfMemoryError: PermGenspace

问题三:GC 算法

复制(Copying)算法,我前面讲到的新生代 GC,基本都是基于复制算法,将活着的对象复制到 to 区域,拷贝过程当中将对象顺序放置,就能够避免内存碎片化。这么作的代价是,既然要进行复制,既要提早预留内存空间,有必定的浪费;另外,对于 G1 这种分拆成为大量 region 的 GC,复制而不是移动,意味着 GC 须要维护 region 之间对象引用关系,这个开销也不小,不管是内存占用或者时间开销。

标记 - 清除(Mark-Sweep)算法,首先进行标记工做,标识出全部要回收的对象,而后进行清除。这么作除了标记、清除过程效率有限,另外就是不可避免的出现碎片化问题,这就致使其不适合特别大的堆;不然,一旦出现 Full GC,暂停时间可能根本没法接受。

标记 - 整理(Mark-Compact),相似于标记 - 清除,但为避免内存碎片化,它会在清理过程当中将对象移动,以确保移动后的对象占用连续的内存空间。

问题四: G1 垃圾回收器采用的是什么垃圾回收算法?

从 GC 算法的角度,G1 选择的是复合算法,能够简化理解为:

在新生代,G1 采用的仍然是并行的复制算法,因此一样会发生 Stop-The-World 的暂停。

在老年代,大部分状况下都是并发标记,而整理(Compact)则是和新生代 GC 时捎带进行,而且不是总体性的整理,而是增量进行的。

问题五:GC 调优思路

从性能的角度看,一般关注三个方面,内存占用(footprint)、延时(latency)和吞吐量(throughput),大多数状况下调优会侧重于其中一个或者两个方面的目标,不多有状况能够兼顾三个不同的角度。固然,除了上面一般的三个方面,也可能须要考虑其余 GC 相关的场景,例如,OOM 也可能与不合理的 GC 相关参数有关;或者,应用启动速度方面的需求,GC 也会是个考虑的方面。 基本的调优思路能够总结为:

理解应用需求和问题,肯定调优目标。假设,咱们开发了一个应用服务,但发现偶尔会出现性能抖动,出现较长的服务停顿。评估用户可接受的响应时间和业务量,将目标简化为,但愿 GC 暂停尽量控制在 200ms 之内,而且保证必定标准的吞吐量。

掌握 JVM 和 GC 的状态,定位具体的问题,肯定真的有 GC 调优的必要。具体有不少方法,好比,经过 jstat 等工具查看 GC 等相关状态,能够开启 GC 日志,或者是利用操做系统提供的诊断工具等。例如,经过追踪 GC 日志,就能够查找是不是 GC 在特定时间发生了长时间的暂停,进而致使了应用响应不及时。

选择的 GC 类型是否符合咱们的应用特征,若是是,具体问题表如今哪里,是 Minor GC 过长,仍是 Mixed GC 等出现异常停顿状况;若是不是,考虑切换到什么类型,如 CMS 和 G1 都是更侧重于低延迟的 GC 选项。

经过分析肯定具体调整的参数或者软硬件配置。验证是否达到调优目标,若是达到目标,便可以考虑结束调优;不然,重复完成分析、调整、验证这 个过程。

问题六:如何提升JVM的性能?

新对象预留在年轻代 经过设置一个较大的年轻代预留新对象,设置合理的 Survivor 区而且提供 Survivor 区的使用率,能够将年轻对象保存在年轻代。

大对象进入年老代 使用参数-XX:PetenureSizeThreshold 设置大对象直接进入年老代的阈值

设置对象进入年老代的年龄 这个阈值的最大值能够经过参数-XX:MaxTenuringThreshold 来设置,默认值是 15

稳定的 Java 堆 得到一个稳定的堆大小的方法是使-Xms 和-Xmx 的大小一致,即最大堆和最小堆 (初始堆) 同样。

增大吞吐量提高系统性能 –Xmx380m –Xms3800m:设置 Java 堆的最大值和初始值。通常状况下,为了不堆内存的频繁震荡,致使系统性能降低,咱们的作法是设置最大堆等于最小堆。假设这里把最小堆减小为最大堆的一半,即 1900m,那么 JVM 会尽量在 1900MB 堆空间中运行,若是这样,发生 GC 的可能性就会比较高; -Xss128k:减小线程栈的大小,这样可使剩余的系统内存支持更多的线程; -Xmn2g:设置年轻代区域大小为 2GB; –XX:+UseParallelGC:年轻代使用并行垃圾回收收集器。这是一个关注吞吐量的收集器,能够尽量地减小 GC 时间。 –XX:ParallelGC-Threads:设置用于垃圾回收的线程数,一般状况下,能够设置和 CPU 数量相等。但在 CPU 数量比较多的状况下,设置相对较小的数值也是合理的; –XX:+UseParallelOldGC:设置年老代使用并行回收收集器。

尝试使用大的内存分页 –XX:+LargePageSizeInBytes:设置大页的大小。 内存分页 (Paging) 是在使用 MMU 的基础上,提出的一种内存管理机制。它将虚拟地址和物理地址按固定大小(4K)分割成页 (page) 和页帧 (page frame),并保证页与页帧的大小相同。这种机制,从数据结构上,保证了访问内存的高效,并使 OS 能支持非连续性的内存分配。

使用非占有的垃圾回收器 为下降应用软件的垃圾回收时的停顿,首先考虑的是使用关注系统停顿的 CMS 回收器,其次,为了减小 Full GC 次数,应尽量将对象预留在年轻代。

问题七:system.gc() 的做用是什么?

问题八:JVM类加载过程

问题九:类加载器的类型

问题十一:上下文类加载器

问题十二:自定义类加载器

问题十三:动态代理的原理

问题十四:动态代理:JDK动态代理和CGLIB代理的区别?

问题十五:CGlib比JDK快?

总结

因为篇幅过长的缘由,为了避免影响你们的阅读效果,文中没有给到全部的答案。我这里以文件的形式整理好了,须要借阅的程序员朋友能够免费来领取。还有个人JVM学习笔记Xmind文件也免费分享给有须要朋友!(缩略图未展开)

 

分享免费架构学习资料

 

欢迎工做一到五年的Java工程师朋友们加入Java高级架构:705127209

里面提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)

合理利用本身每一分每一秒的时间来学习提高本身,不要再用"没有时间“来掩饰本身思想上的懒惰!趁年轻,使劲拼,给将来的本身一个交代!

相关文章
相关标签/搜索