关于jvm调优:记一次jvm调优及垃圾收集器

本文在第一段先简略解说调优的原因和过程,具体波及到的知识点,在前面段中具体介绍。 1. 调优过程1.1. 问题定位有一天忽然收到监控告警,大批量产线服务实例在主动重启。于是连忙上平台下载dump日志,以及查看其余监控事件,最终定位到问题: 那几分钟内,涌入几十万用户登录平台操作,导致内存吃紧,简直每个实例都触发了几次 Full GC。而因为集中性的 Full GC,STW 工夫过长,服务测活接口长期调不通,k8s断定服务故障,就重启pod。 问题定位了,除了优化代码,缩小有效内存大量占用以外,还能够调优一下Jvm参数了。 1.2. gc 问题定位既然是 gc 出的问题,那就通过 jstat -gcutil pid 时长距离 命令,实时看一下gc的过程状态。 通过一段时间察看,发现每次 young gc 后,survivor 区域中占用比例很高(近百分之百),甚至某些次 old 区域中有稍微增长。这阐明一个问题: young gc 后存活的对象太多,survivor区寄存不下,溢出的对象就间接进入了老年代。这就放慢了老年代内存的占用速度,提前须要 full gc。 gc 的问题也定位到了,接下来分几个步骤优化 1.3. gc 优化分了几个维度: 最直观体现是 survivor 区有余,因而能够加大一下年老代中 survivor 比例,即缩小-XX:SurvivorRatio(eden区和单个survivor区的比例,默认值:8)的值。略微加大一下年老代的占比,即缩小-XX:NewRatio(老年代和年老代的比例,默认值:2)的值。最基本的还是内存不足,所以如果能够,加大xms/xmx。调优垃圾收集器,缩小 full gc 中 stw 的工夫,防止测活接口长时间停机。因为之前是jdk 8默认的(Parallel Scavenge + Parallel Old),换成了更适宜高并发的(ParNew + CMS)。2. jvm命令及参数2.1. jstat -gcutil留神:出于窃密思考,理论的数据不能在文中展现,下列展现的是与上下文无关的服务数据,是比拟失常的 gc 过程数据: [root@xxxapi data]# jstat -gcutil 1 1000 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 50.07 0.00 53.80 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 57.11 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 62.04 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 64.26 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 66.61 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 69.18 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 71.68 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 74.75 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 77.20 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 80.12 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 83.09 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 87.77 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 89.82 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 92.29 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 94.57 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 50.07 0.00 99.00 13.25 89.91 86.07 1082 21.299 5 2.055 23.354 0.00 74.61 3.38 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 7.88 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 11.46 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 14.93 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 18.23 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 21.29 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 25.22 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 28.74 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 31.64 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 36.85 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 39.30 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 44.76 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 48.55 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 51.25 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 54.17 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 58.48 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 61.99 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 64.52 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 67.25 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 70.92 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 74.60 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 78.43 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 82.41 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 86.26 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 90.79 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 93.74 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 95.90 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 0.00 74.61 99.28 13.25 89.91 86.07 1083 21.325 5 2.055 23.380 51.43 0.00 2.69 13.29 89.91 86.07 1084 21.345 5 2.055 23.400 51.43 0.00 5.53 13.29 89.91 86.07 1084 21.345 5 2.055 23.400 51.43 0.00 8.21 13.29 89.91 86.07 1084 21.345 5 2.055 23.400S0:幸存1区以后应用比例S1:幸存2区以后应用比例E:伊甸园区应用比例O:老年代应用比例M:元数据区应用比例CCS:压缩应用比例YGC:年老代垃圾回收次数YGCT:年老代垃圾回收耗费工夫FGC:老年代垃圾回收次数FGCT:老年代垃圾回收耗费工夫GCT:垃圾回收耗费总工夫2.2. 对象进入老年代路径1. 对象年龄达到阈值后进入老年代默认状况下,对象在新生代经验了15次GC后,便会达到进入老年代的条件,将对象转移进入老年代。当然,年龄的阈值能够通过JVM参数进行设置: ...

March 27, 2023 · 4 min · jiezi

关于jvm调优:为什么要设置两个Survivor区

为什么一个Survivor区不行?第一局部中,咱们晓得了必须设置Survivor区。假如当初只有一个survivor区,咱们来模仿一下流程:刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被挪动到Survivor区。这样持续循环上来,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很显著这两局部对象所占有的内存是不间断的,也就导致了内存碎片化。我绘制了一幅图来表明这个过程。其中色块代表对象,红色框别离代表Eden区(大)和Survivor区(小)。Eden区天经地义大一些,否则新建对象很快就导致Eden区满,进而触发Minor GC,有悖于初衷。碎片化带来的危险是极大的,重大影响Java程序的性能。堆空间被分布的对象占据不间断的内存,最间接的后果就是,堆中没有足够大的间断内存空间,接下去如果程序须要给一个内存需要很大的对象分配内存。。。画面太美不敢看。。。这就好比咱们爬山的时候,背包里所有货色紧挨着放,最初就可能省出一块残缺的空间放相机。如果每件行李之间隔一点空隙乱放,很可能最初就要一路把相机挂在脖子上了。那么,牵强附会的,应该建设两块Survivor区,刚刚新建的对象在Eden中,经验一次Minor GC,Eden中的存活对象就会被挪动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程十分重要,因为这种复制算法保障了S1中来自S0和Eden两局部的存活对象占用间断的内存空间,防止了碎片化的产生)。S0和Eden被清空,而后下一轮S0与S1替换角色,如此周而复始。如果对象的复制次数达到16次,该对象就会被送到老年代中。下图中每局部的意义和上一张图一样,就不加正文了。上述机制最大的益处就是,整个过程中,永远有一个survivor space是空的,另一个非空的survivor space无碎片。那么,Survivor为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果Survivor区再细分上来,每一块的空间就会比拟小,很容易导致Survivor区满,因而,我认为两块Survivor区是通过衡量之后的最佳计划。原文:https://www.cnblogs.com/duanxz/p/6076662.html

March 26, 2023 · 1 min · jiezi

关于jvm调优:谈JVM参数GC线程数ParallelGCThreads合理性设置

作者:京东批发 刘乐 1. ParallelGCThreads参数含意在讲这个参数之前,先谈谈JVM垃圾回收(GC)算法的两个优化标的:吞吐量和进展时长。JVM会应用特定的GC收集线程,当GC开始的时候,GC线程会和业务线程抢占CPU工夫,吞吐量定义为CPU用于业务线程的工夫与CPU总耗费工夫的比值。为了承接更大的流量,吞吐量越大越好。 为了平安的垃圾回收,在GC或者GC某个阶段,所有业务线程都会被暂停,也就是STW(Stop The World),STW持续时间就是进展时长,进展时长影响响应速度,因而越小越好。 这两个优化指标是有抵触的,在肯定范畴内,参加GC的线程数越多,进展时长越小,但吞吐量也越小。生产实践中,须要依据业务特点设置一个正当的GC线程数,获得吞吐量和进展时长的均衡。 目前宽泛应用的GC算法,包含PS MarkSweep/PS Scavenge, ConcurrentMarkSweep/ParNew, G1等,都能够通过ParallelGCThreads参数来指定JVM在并行GC时参加垃圾收集的线程数。该值设置过小,GC暂停工夫变长影响RT,设置过大则影响吞吐量,从而导致CPU过高。 2. ParallelGCThreads参数设置GC并发线程数能够通过JVM启动参数: -XX:ParallelGCThreads=<N>来指定。在未明确指定的状况下,JVM会依据逻辑核数ncpus,采纳以下公式来计算默认值: ◦当ncpus小于8时,ParallelGCThreads = ncpus ◦否则 ParallelGCThreads = 8 + (ncpus - 8 ) ( 5/8 ) 一般来说,在无特殊要求下,ParallelGCThreads参数应用默认值就能够了。然而在JRE版本1.8.0_131之前,JVM无奈感知Docker的CPU限度,会应用宿主机的逻辑核数计算默认值。 比方部署在128核物理机上的容器,JVM中默认ParallelGCThreads为83,远超过了容器的核数。过多的GC线程数抢占了业务线程的CPU工夫,加上线程切换的开销,较大的升高了吞吐量。因而JRE 1.8.0_131之前的版本,未明确指定ParallelGCThreads会有较大的危险。 3. ParallelGCThreads参数试验创立 8C12G 容器,宿主机是128C。模仿线上实在流量,采纳雷同QPS,察看及比照JVM YoungGC,JVM CPU,容器CPU等监控数据。场景如下: ◦场景1: JVM ParallelGCThreads 默认值,QPS = 420,继续5分钟,CPU恒定在70% ◦场景2: JVM ParallelGCThreads=8,QPS = 420,继续5分钟,CPU恒定在65% ◦场景3: JVM ParallelGCThreads 默认值,QPS刹时发压到420,前1min CPU继续100% ◦场景4: JVM ParallelGCThreads=8,QPS刹时发压到420,前2s CPU继续100%,前面回落 从监控数据来看,各场景下CPU差距较显著,特地是场景3和场景4的比照。场景3因为GC线程过多,CPU继续100%时长达1分钟。能够得出以下两个论断: 1.批改 ParallelGCThreads = 8后,等同QPS状况下,CPU会升高5%左右 2.批改 ParallelGCThreads = 8后,霎时发压且CPU打满状况下,CPU复原较快 ...

February 17, 2023 · 1 min · jiezi

关于jvm调优:G1垃圾回收器在并发场景调优

一、序言目前企业级支流应用的Java版本是8,垃圾回收器反对手动批改为G1,G1垃圾回收器是Java 11的默认设置,因而G1垃圾回收器能够用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优化。 为了简化探讨,上面假如针对4C/16G物理机器进行优化。 二、G1概览(一)理解G11、最大堆大小G1治理的最大堆大小为64G。每个Region的大小通过-XX:G1HeapRegionSize来设置,大小为1~32MB,默认最多能够有2048个Region,G1能治理的最大堆内存是32MB*2048=64G。 应用G1垃圾回收器最小堆内存应为1MB*2048=2GB,低于此值倡议应用其它垃圾回收器。 2、Region大小Region大小为1~32MB,具体取值有1MB、2MB、4MB、8MB、16MB、32MB,Region大小优化与大对象无关,当对象占用内存超过Region的一半时将被视为大对象。 被标记为大对象将不利于垃圾回收。 3、获取默认值查看本地JVM特地是G1垃圾回收器以后的默认值。 java -XX:+PrintFlagsInitial >> ~/1.txt(二)三种GC模式G1垃圾回收器有两种垃圾回收模式,新生代回收和混合回收,非凡状况下会切换到Full GC。 1、新生代回收新生代回收在最大进展工夫内,会解决所有Eden区的垃圾。具体操作是将Eden区所有存活的对象复制到Survivor区,同时清空Eden区。 新生代回收随同着利用暂停,最长进展工夫不超过最大进展工夫,新生代回收只管有暂停机制,思考到并行回收的个性,回收逻辑绝对简略,回收效率仍然较高。一般而言,新生代回收理论耗时通常低于最大进展工夫。 新生代回收触发机会是新创建的对象在Eden区找不到足够的存储空间。 2、混合回收混合回收随同着新生代回收和老年代回收,在最大进展工夫范畴内,会解决大部分Eden区的垃圾和一部分老年代垃圾。 老年代回收毫无疑问会随同着利用暂停。混合回收操作比较复杂,绝对新生代回收来说,单位工夫回收的垃圾数要少,回收效率要低。一般而言,混合回收的理论耗时通常靠近或者等于最大进展工夫。 混合回收触发机会是由参数InitiatingHeapOccupancyPercent管制,默认值为45,含意是老年代占用空间大小与堆的总大小比值超过此数便会触发混合回收。 默认值45%是比拟正当的,不倡议所谓的调优。老年代回收策略同样是将选定Region区内存活的对象复制到闲暇Region区,混合回收随同着回收新生代垃圾可能清理出更大的闲暇Region区来寄存老年区存活对象,保障回收过程可能失常进行。 老年区存活对象个别较多,对象在内存中复制耗时较长,因而相对来说混合回收效率较低。 3、Full GCFull GC是所有G1垃圾回收调优者尽力回避的状况,单线程回收垃圾,回收对象是整个堆,不再受最长进展工夫束缚,一旦呈现此状况,意味着利用的响应工夫有情的变长。 当利用不定期进入Full GC状态时,与其任由其单线程重塑堆内存,不如采纳冗余策略,在流量低谷时刻,逐个重启利用,被动重塑堆内存空间。 流量高峰期呈现Full GC景象及其应答策略前面再探讨。 (三)默认参数1、堆内存参数默认值阐明优化倡议MaxGCPauseMillis200ms最大进展工夫 G1HeapRegionSize 不设置时启发式推断 G1NewSizePercent5新生代最小百分比 G1MaxNewSizePercent60新生代最大百分比 2、新生代内存回收参数默认值阐明优化倡议ParallelGCThreads 并行GC线程数,会依据CPU核数推断默认值MaxTenuringThreshold15从新生代降职到老年代年龄阈值 SurvivorRatio8Eden和一个Survivor的比例 TargetSurvivorRatio50Survivor区内存使用率,增大该值会升高到老年代概率 +G1EagerReclaimHumongousObjectstrue是否在YGC时回收大对象 3、混合回收参数默认值阐明优化倡议G1MixedGCCountTarget8值越大,收集老年代分区越少 G1OldCSetRegionThresholdPercent10示意一次最多收集10%的分区 三、垃圾在堆中流转垃圾回收器调优的要害是尽可能减少Mixed GC的频率,换句话说尽可能减少垃圾流转到老年代。GC调优便是意识垃圾在堆中的流转法则,从而对流向老年代的垃圾予以提前干预,使之尽可能留在新生代。 垃圾在新生代(次要指Eden区)中,垃圾回收应用YGC,回收线程与利用线程并发进行,垃圾回收对利用通明进行,如果CPU算力短缺的话,利用简直感觉不到垃圾在回收进行。 垃圾在老年代中,垃圾回收采纳Mixed GC,回收线程开始工作时,利用线程阻塞,期待回收线程工作结束有,利用线程从新被唤醒。频繁的Mixed GC对利用的吞吐量产生不良影响。 1、对象如何进入老年代一般而言,新创建的对象会存在于新生代的Eden区,下一次垃圾回收处罚便间接回收了。如果对象比拟倔强(持续被其它对象援用),那么会在Survivor区流转,每GC一次,依然不能被垃圾回收,那么年龄加一,持续在S0和S1区流转,当年龄增长到肯定的阈值,间接进入老年代。 (1)大对象间接到老年代 新创建的对象如果过大,那么不通过新生代,间接进入老年代。管制对象大小阈值有参数-XX:PretenureSizeThreshold决定,单位字节。 (2)动静年龄判断 除了对象在S0和S1区重复流转年龄变动外,垃圾回收保护另外一套独立的年龄断定规定:如果YGC后尚未被回收的垃圾超过了Survivor区的50%,那么超过的这批对象会间接进入老年代。 12G * 60% * 10% * 50% * 1024 = 737MB动静年龄断定规定要求每次YGC尽可能的彻底,意味着每次GC的最长工夫不能太短,默认200毫秒是比拟正当的值。 如果预设置的最长进展工夫过短,那么每次GC后存活大量尚未被回收的垃圾,S区容量无限,不该进入老年代的垃圾疾速在老年代沉积,频繁的Mixed GC不可避免。 2、高并发减速进入老年代在高并发场景下,CPU和内存资源吃紧,负载很高,不确定的性能抖动减速垃圾进入老年代。 举例说明,DAO层查询数据库,一次残缺的会话完结后,整个会话中产生的对象垃圾在Eden区该当被全副回收。因为网络稳定,数据库解决能力的限度,大量会话超时。在此过程中这部分对象垃圾很可能在疾速S0和S1流转中叠加年龄,或者触发动静年龄断定,间接进入老年代。 老年代内存空间不够用,触发Mixed GC,Mixed GC间接副作用是利用卡顿。 ...

March 18, 2022 · 1 min · jiezi

关于jvm调优:Java性能调优实战怎样才能做好性能调优

对于性能调优,我先来说说的我的感触。Java 性能调优不像是学一门编程语言,无奈通过直线式的思维来把握和利用,它对于工程师的技术广度和深度都有着较高的要求。 互联网时代,一个简略的零碎就囊括了应用程序、数据库、容器、操作系统、网络等技术,线上一旦呈现性能问题,就可能要你协调多方面组件去进行优化,这就是技术广度;而很多性能问题呢,又暗藏得很深,可能因为一个小小的代码,也可能因为线程池的类型抉择谬误…可归根结底考验的还是咱们对这项技术的理解水平,这就是技术深度,显然,性能调优不是一件容易的事。 《Java 性能调优实战》将从实战登程,精选高频性能问题,透过 Java 底层源码,提炼出优化思路和它背地的实现原理,最初造成一套“学完就能用的调优方法论”.这也是很多一线大厂对于高级工程师的要求,心愿通过此篇文章帮忙你疾速进阶。联合 Java 利用开发的知识点,小编将内容分为七大模块,从上到下顺次详解 Java 应用服务的每一层优化实战! 因为内容较多,本次将展现局部,如果看得不过瘾想更加深刻地理解本笔记彻底把握 Java 后端性能调优间接【戳此处】即可收费获取! 模块一:概述为你建设两个规范。一个是性能调优规范,通知你能够通过哪些参数去掂量零碎性能;另一个是调优过程规范,带你理解通过哪些严格的调优策略,咱们能够排查性能问题,从而解决问题。 模块二:Java 编程性能调优JDK 是 Java 语言的根底库,相熟 JDK 中各个包中的工具类,能够帮忙你编写出高性能代码。这里我会从根底的数据类型讲起,波及容器在理论利用场景中的调优,还有当初互联网零碎架构中比拟重要的网络通信调优。 模块三:多线程性能调优目前大部分服务器都是多核处理器,多线程编程的利用宽泛。为了保障线程的安全性,通常会用到同步锁,这会为零碎埋下很多隐患;除此之外,还有多线程高并发带来的性能问题,这些都会在这个模块重点解说。 模块四:JVM 性能监测及调优Java 应用程序是运行在 JVM 之上的,对 JVM 进行调优能够晋升零碎性能。这里重点解说 Java 对象的创立和回收、内存调配等。 模块五:设计模式调优在架构设计中,咱们常常会用到一些设计模式来优化架构设计。这里将联合一些简单的利用场景,分享设计优化案例。 模块六:数据库性能调优数据库最容易成为整个零碎的性能瓶颈,这里会重点解析一些数据库的罕用调优办法。 模块七:实战演练场 以上六个模块的内容,都是基于某个点的调优,当初是时候把你后面所学都调动起来了,这里将带你进入综合性能问题高频呈现的利用场景,学习整体调优办法。如果看得不过瘾想更加深刻地理解本笔记彻底把握 Java 后端性能调优间接【戳此处】即可收费获取!

December 24, 2021 · 1 min · jiezi

关于jvm调优:面试Java岗老喜欢盯着JVM问有那么多项目要调优吗

我是感觉当初不少公司面试Java程序员就盯着jvm问,是种本末倒置。而我最不能忍的就是好多程序员解决问题喜爱一杆子捅到底层,遇到问题居然会先想是不是JVM的bug,遇到延时高就会判定是gc算法有问题,居然会先去思考替换gc算法,这种自信我也是挺醉的。 那么真有那么多我的项目要调优吗? 个别我的项目必定是不须要进行 JVM 调优的,因为 JVM 自身就是为这种低延时、高并发、大吞吐的服务设计和优化的,咱们很少须要去扭转什么。所以,咱们往往更偏重于应用服务自身的调优。 在一些利用中,比方大数据计算引擎,是一种十分极其的 JVM 利用,对延时的要求并不高,但对吞吐量要求很高,会有大量的短生命周期对象产生,同时也有大量的对象生存工夫十分久,咱们就须要对特定的一些 JVM 参数进行批改。 再比方生产环境中呈现内存溢出,咱们须要判断是因为大峰值下没有限流,霎时创立大量对象而导致的内存溢出,还是是因为内存透露而导致的内存溢出。对于内存透露导致的,这种问题就是程序的 Bug,咱们须要及时找到问题代码进行批改,而不是调整 JVM。 JVM 在很大水平上加重了 Java 开发人员投入到对象生命周期治理的精力。在应用对象的时候,JVM 会主动分配内存给对象,在不应用的时候,垃圾回收器会主动回收对象,开释占用的内存。所以个别状况下咱们是不须要调优的。当然事无相对,某些非凡场景就须要咱们进行参数调整,但调整的前提肯定是你对 JVM 的运行原理十分相熟才行,所以面试问到JVM也是十分失常的事件。 如果你对于以上性能优化的准则、档次、通用办法以及代码品质的关系基本没有一个概念,也不晓得何从下手的话。LZ 举荐大家看一下我上面要为大家介绍的阿里 2021 最新版的性能优化全解小册~先展现局部截图,想要完整版PDF文档的,点这里就能够收费获取了。 小册内容从如何制订性能调优与策略开始,把 Java 编程、多线程、JVM、设计模式、数据库五个方面的性能优化细节陈说得清清楚楚,最初还把阿里双十一性能调优实战过程残缺的出现给读者,大家看了之后就晓得到底有多香了~ 不多 bb,来看内容 阿里性能优化全解小册(2021 最新版) 因为小册一共有近 500 页之多,篇幅限度必定无奈将全部内容展现进去,须要完整版的小伙伴点赞加珍藏,关注我之后增加小助理 , 即可获取收费下载方式 目录总览 内容节选 Java 编程性能调优 多线程性能调优 JVM 性能监测及调优 设计模式调优 数据库性能调优 实战篇 设计一个更优的分布式锁 电商零碎的分布式事务调优 应用缓存优化零碎性能 双十—抢购性能瓶颈调优 最初 性能调优就是一场持久战!即便你的产品上线之后,还须要继续开发,很多因素都会带来性能问题,想要真正扛起公司性能的大梁,就不要拘泥于本人公司的业务,无妨多去看看他人家的性能优化(比方行业大佬阿里)。 ...

December 3, 2021 · 1 min · jiezi

关于jvm调优:JVM基于交换IO优化的Java虚拟机垃圾回收

基于替换IO优化的Java虚拟机垃圾回收(ACM论文笔记)ABSTRACT因为跨平台的可移植性,各种应用程序、框架和服务都构建在Java虚拟机(JVM)上(例如,大数据分析)。然而,它们中的许多都存在垃圾收集(GC)的长提早,这也升高了零碎的吞吐量、效率和可用性。例如,当客户端须要比可用零碎内存更大的内存时,操作系统(OS)通常应用它的替换空间来开释存储在内存中的一些非流动内容。在这种状况下,因为I/O工夫(即换入/换出),GC提早可能会更长。本文对JVM中现有的GC策略进行了性能剖析。基于剖析后果,咱们提出了一种无效的GC计划,通过替换I/O优化来补充现有的GC策略来进步GC性能。在这个计划中,咱们通过在GC期间与OS替换零碎交互来选择性地压缩JVM堆。试验结果表明,咱们的计划别离缩小了77.5%的GC开销和82.5%的吞吐量。 INTRODUCTION目前,Java因为其跨平台的可移植性,在大数据分析系统和分布式系统等许多零碎中流行起来。这些零碎的一些驰名例子包含Hadoop[1]和Spark[2]。此外,一个宽泛应用的机器学习(ML)库DeepLeaning4Java (DL4J)[4]也是用Java编写的,能够与大数据分析系统或分布式系统相结合,不便开发,高效剖析数据。在云环境或虚拟机中执行这些零碎须要大量资源。在这些资源需要中,零碎内存需要是要害的,通常是不可预测的。例如,应用虚拟机的客户机可能不晓得彼此的资源应用模式以及物理机上的理论资源应用状况。此外,在执行ML工作负载时,很难晓得两头过程中产生的长期数据的大小。即便产生这种不可预测的状况,零碎也应该确保可用性。因而,如果每个客户端的内存需要超过物理内存总容量,则现有操作系统提供替换零碎,将未激活或最近起码应用(LRU)页面替换到磁盘上的替换空间中。如果零碎无奈解决此问题,应用程序将以内存不足谬误(OOME)异样终止。这些Java应用程序在Java虚拟机(JVM)上执行。JVM中内存治理最重要的组件之一是垃圾收集(GC)。GC是一种主动的内存治理技术,它回收JVM堆空间中未援用的数据。此工作为Stop-The-World (STW)格调,这意味着应用程序将在此工作期间齐全进行,从而影响应用程序的性能。并行清理GC (Parallel screpinggc, PSGC)是OpenJDK 8的现有策略,它应用滑动压缩,将援用的数据复制到堆左侧的空白空间中,以防止碎片化。尽管GC是回收JVM中未援用数据的要害组件,但它会生成不必要的替换I/O操作。例如,当应用替换I/O操作触发带有PSGC策略的GC操作时,替换出的LRU数据可能会被不必要地替换进来,因为GC的压缩过程可能须要替换出的数据。另外,依据PSGC策略,换入的数据能够立刻从新换出。这是因为JVM和OS替换零碎彼此是不可见的。这种不必要的磁盘I/O将提早STW,并对应用程序吞吐量产生不利影响,因为拜访磁盘的提早通常比拜访内存慢10,000倍。为了解决这一问题,以往的钻研[24,27]提出了以下解决方案。Veldema等人的[24]通过绑定和压缩指标对象来缩小交换量。Zhuang等人的[27]通过缩小宰割页面所波及的开销来缩小STW。在解决内存压力下的零碎替换问题方面,咱们的钻研与这些工作统一[24,27]。为了缩小替换I/O的开销,[24]最小化了指标数据大小,[27]打消了宰割的开销。相同,咱们次要通过选择性地压缩JVM堆来优化替换I/O。在本文中,咱们提出了一种高效的JVM GC操作计划,以进步Java应用程序的性能,次要有以下两种计划:(1)咱们让JVM通过接口与操作系统交互。该计划容许零碎替换信息(例如,数据是否被替换),以交付给JVM。(2)咱们提出了一个选择性压缩GC, JVM依据下面来自操作系统的提醒有选择地压缩堆。该计划容许操作系统防止不必要的磁盘I/O,从而进步应用程序的吞吐量。咱们在OpenJDK 8的PSGC中实现了上述计划。咱们应用SPECjvm2008[11]和DaCapo[3]来评估咱们的零碎,这是行业标准的基准。另外,咱们应用SparkBench[10]中的图计算工作负载来评估咱们的工作负载。结果表明,该零碎的利用性能进步了82.5%。

November 14, 2021 · 1 min · jiezi

关于jvm调优:深入理解jvm-编译优化下

前言本文接上文的内容持续讲述:深刻了解jvm - 编译优化(上) 概述补充后端优化的另一项内容提前编译器的解决介绍jvm的几项重点优化措施 办法内联(重要)逃逸剖析(先进)公共子表达式打消(经典)数组边界查看打消(语言经典)后端优化提前编译器提前编译器的历史其实曾经很久了,然而在java畛域晓得andirod的崛起才被java关注,在解说对于提前编译器的关注之前,咱们来看下提前编译器的优劣 长处解决即时编译器在程序中占用运算资源。即时编译器进行缓存减速提前编译的代码品质。 书中提到了过程间剖析指的是什么? 目前的java在过程间剖析优化力度不够,同时因为动态编译的形式能够在全程序进行优化。 java的实际 针对下面的长处,jdk9实现了一个Jactc 提前编译器。说完提前编译器的劣势,上面看下即时编译器的劣势: 性能剖析领导优化:对于一些动静代码和形象办法,能够通过动态分析失去激进型的优化:能够做一些并不保障完全正确的深度优化,并且能够回退到爱护程序。链接时优化:java天生反对即时编译产生本地代码。 对于提前编译的内容只须要根本理解即可。上面咱们来看下对于jvm更多的底层优化。 底层优化上面是对于jvm的底层优化内容,jvm的底层优化内容十分多,比方:办法内联、冗余反复打消、复写流传、无用代码打消等等。这里筛选了书中的几项内容进行介绍: 办法内联(重要)逃逸剖析(先进)公共子表达式打消(经典)数组边界查看打消(语言经典)办法内联含意:即把被内联的办法搬到内联块的外部。简略来说就是把多层办法嵌套调用合并到一个办法,缩小栈桢沉积。 如何实现? 实现的前提条件:首先必须是 非虚办法,即能够不通过虚办法调用的静态方法。 C和C++应用明确虚和非虚办法布局界线。Java的具体实现:引入类型继承关系剖析和实现,确定在目前已加载的类中,某个接口是否有多于一种的实现、某个类是否存在子类、某个子类是否笼罩了父类的某个虚办法等信息”。 上方简略来说能够概括为上面这几点: 确定接口的实现者以及是否能够实现是否有继承关系是否存在重写办法办法逃逸逃逸剖析的基本原理是:剖析对象动静作用域,当一个对象在办法外面被定义后,它可能被内部办法所援用,例如作为调用参数传递到其余办法中。 一个对象在办法定义中有可能被内部援用,这又引申出一个重要的优化伎俩:栈上调配,栈上调配简略来说就是能够缩小堆空间的开拓并且进步内存的回收效率。 java只反对办法的逃逸,并不反对线程的逃逸。书中介绍目前逃逸剖析的状况如下: 改良空间十分大jdk6才初步反对波及简单的算法另一项优化形式是标量替换: 什么是标量:如果变量无奈更少的单位示意(int, byte, boolean等),那么这些变量成为标量。 什么是聚合量:能够持续合成的叫做聚合量。 标量替换:把一个java对象拆散,依据程序的拜访状况将其用于成员变量复原和拜访。 联合逃逸剖析和标量替换,逃逸剖析会把不能被内部拜访并且能够被标量替换示意的对象进行不创建对象。同样再次强调只反对办法内解决,不反对办法逃逸。而后是同步打消的优化: 同步打消:线程同步自身是一个绝对耗时的过程,如果逃逸剖析可能确定一个变量不会逃逸出线程,无奈被其余线程拜访,那么这个变量的读写必定就不会有竞争,对这个变量施行的同步措施也就能够平安地打消掉。 最初如果一个变量不会呈现逃逸,那么动解除同步措施。 公共子表达式什么是公共子表达式?如果一个表达式E之前曾经被计算过了,并且从先前的计算到当初E中所有变量的值都没有发生变化,那么E的这次呈现就称为公共子表达式。对于这种表达式,没有必要花工夫再对它从新进行计算,只须要间接用后面计算过的表达式后果代替E。 案例: int d = (c * b) * 12 + a + (a + b * c);如果此时表达式被计算过一遍,他会被替换为上面的形式: int d = E * 12 + a + (a + E);数组边界查看打消java的数组和c以及c++的数组不同他并不是裸指针的形式操作数组,为了保障数组的拜访平安,jvm的底层在每次的操作的时候都须要对于数组的边界进行查看操作,即一个含头不含尾的判断:[start, end). 针对这个问题,java是通过如下的形式思考优化的: 如果能够界定数组拜访范畴,实践上能够对消数组拜访的耗费提前到编译期间实现隐式异样解决:比方空指针和除数为0的异样。最终解决形式: 应用一个segment fault 信号进行替注册,保障少数拜访不为null时候不进行判断为空的操作。一旦异样则转到异样处理器解决并且抛出异样。 ...

August 29, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-Shenadoah

深刻了解JVM - Shenadoah前言 zgc和shenadoah的收集器是面向未来的收集器,目前还处于不断完善的阶段,尽管咱们平时可能不太用的上,然而理解和根本把握它是必须的,对于这一块网上的内容的确比拟少,所以集体还是应用了书本外面的内容进行总结。 另外这两个垃圾收集器是齐全舍弃分代这个概念的,留神是齐全舍弃,并不是相似G1收集器尽管应用了分区然而实质上还是分代收集的收集器。 因为这两个收集器的内容较多,这里离开进行解说,本篇解说Shenadoah收集器。 思维导图:不想看文字的,能够查看思维导图:https://www.mubucm.com/doc/7L... 概述低提早垃圾收集器 在正式介绍之前,有必要阐明一下整个背景,古代的垃圾收集器思考的点次要为上面这三个条件:内存占用,吞吐量,提早,通过之前的收集器介绍,咱们晓得了尽管支流的g1收集器在标记阶段实现了并发,然而在初始标记和筛选回收阶段还是须要进行阶段性的stop world的,这个垃圾收集器并没有做到真正意义上的并发,并且因为分区+region分代的设计限度,必然会产生垃圾收集的进展。所以将来的垃圾收集器次要指标将会是面向极低提早进军,也就是努力实现用户线程和垃圾收集器线程的齐全并发运行。 值得一提的是尽管新生的低提早垃圾收集器摈弃了分代的概念,然而G1的Region分块以及垃圾进展模型保留了下来,咱们也能够看到简直所有的垃圾收集器都是基于前人的致力成绩进行改良,所以不须要非常恐怖内容很难或者是齐全颠覆想法。 g1和cms实现并发的细节 之前的文章提到了增量更新和原始快照,cms应用的是增量更新,g1应用的是原始快照,另外cms应用标记-革除的算法,免不了内存碎片,而g1尽管应用标记-整顿,然而究竟还是须要进行暂停的,所以这是一个十分辣手的问题。 Shenadoah简介 这款收集器是首款非jdk官网开发的垃圾收集器,由redhat公司开发,后续被捐献给eclipse基金会,目前由eclipse基金会进行保护和治理。尽管Shenadoah从设计的细节来看有很多须要欠缺的中央,然而的确曾经具备了独立作为垃圾收集器应用的条件。 比拟惋惜的是oracle因为商业竞争的问题会把shenandoah通过条件编译的伎俩进行排除应用比拟麻烦,所以shenadoah只能存在于openJDK无奈在OracleJdk上进行部署,然而这款垃圾收集器仍然值得咱们学习。 特点 上面来说一下shenadoah的特点: Region 和G1收集器的设计原理一样应用的是region进行分块,同样有着大对象的概念,默认的策略也是依据算法回收最有价值的region。 没有分代和连贯矩阵 留神是没有分代的概念,默认不应用分代收集,换言之就是没有新生代和老年代的说法。那要怎么设计?Shenandoah的解决方案是应用独立构建的“连贯矩阵”全局数据结构来保护region的援用关系,也不要被连贯矩阵这种名词给吓到了,其实实质上是一个二维数组构造,比方咱们在Region N援用了Region M,那么就会在对应的N行M列上打上一个标记,也就是说全局的对象援用都会通过这个表来保护,这也意味着连贯矩阵会随着对象的增长一直收缩。 G1收集器是放弃固定分代而是应用分区的设计,然而分区实质上还是分代的,只不过能够自在决定属于哪一个分代。 上面间接从书外面拷了一张图来显示连贯矩阵的设计: 不得不说的是这个连贯矩阵在设计上是仁者见仁智者见智了,保护一个矩阵尽管很不便然而随着对象的增多会呈现出指数性的表收缩,这样来看还是一个值得商讨的设计,这一点在后续的垃圾收集器zgc介绍中会提到,zgc发现了连贯矩阵的问题,采纳了一些改良伎俩来解决表收缩的问题。反对并发收集和整顿 反对并发收集和整顿,能够实现标记和整顿阶段齐全和用户线程并发执行。 算法细节 那么这个收集器是如何做到这些事件的呢,在介绍工作流程之前,咱们来聊一下算法的实现细节。 brooks pointer 历史起因不过多介绍,这里阐明一下这个值的含意:转发指针。转发指针是什么呢?它是用来解决对象挪动和用户程序并发的一种解决方案。 Brooks pointer的工作原理:就是在对象的构造布局上减少一个新的援用字段,这个援用通常状况下指向本人,当对象产生转移的时候,brooks pointer会指向新援用的地址,这样指向旧援用的对象就能够修复援用指向新对象,这种构造在模式上和JVM的句柄定位相似,都是应用一种间接的拜访模式,差异是转发指针会扩散存在对象头外部。(之前咱们探讨过对象头是动静扩大的格局) 这种设计模式也有点相似于链表的设计模式。 补充:之前如何解决对象援用问题? 应用的是一种在原有的对象内存之上设置爱护陷阱+异样解决的形式,一旦呈现拜访旧对象的行为,就会进入到爱护陷阱当中,并且进入异样处理器进行代码逻辑和援用的修复。这种形式看起来非常的无效,然而如果没有操作系统的反对,就须要通过一直的用户态到内核态的切换,须要消耗更多的上下文切换资源,也是一种十分消耗性能的斗争方法。 毛病: 尽管转发指针被优化到只有一行汇编指令的水平,然而仍然要耗费对象拜访的效率,当然这个计划毫无疑问是比内存陷阱要好, 并发问题: 转发指针的设计意味着他必然有并发的问题,如果产生并发操作,就须要保障写操作必须是在新复制的对象上面,无妨思考上面的问题: 收集器线程复制了新的对象正本用户线程更新对象的某个字段收集器线程更新转发指针的援用值为新正本地址。 如果不防备这三个问题,就会导致用户线程的对象变更都是操作旧对象,所以必须针对指针的拜访操作采取同步的措施。解决办法和对象的援用调配形式也是相似的也是应用CAS+更新失败重试的操作机制。 最初,还须要留神的是Shenadoah必须应用读写屏障去保护brooks pointer(并发问题决定了要时刻放弃同步),这个代价是十分大的。上面咱们接着来讲讲读写屏障的问题。 读写屏障 shenandoah不仅应用了写屏障还应用了读屏障,读屏障也是相似对象援用操作的一个AOP的切面,咱们都晓得对象的读操作必定是要多于写操作的,所以应用读屏障的代价要大很多。 写屏障的概念能够看专栏之前的文章:深刻了解JVM - Hotspot算法细节#写屏障 当然Shenandoah开发者也意识到这个问题,在JDK13的版本中,改用了基于“援用拜访屏障”的形式解决读屏障的问题,“援用拜访屏障”指的是只拦挡对象相似是援用类型的数据进行拜访屏障的拦挡,这样就能够省去一些原生类型并发批改拜访的操作,缩小宏大的读屏障保护开销。 ...

August 13, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-Hotspot算法细节

深刻了解JVM - Hotspot算法细节[TOC] 前言 这一节来专门讨论一下HotSpot的算法的细节内容,内容说难也不难,说容易也的确不容易,有很多要了解的内容,集体在做这次文章的时候,有了更深的了解。 思维导图 如果懒得看文字,这里整顿了一份思维导图帮忙了解: 地址:https://www.mubucm.com/doc/1q... 概述可达性算法的大抵内容和简述,以及JAVA固定GC ROOT的断定条件根节点枚举的实现细节,讲述什么事平安点和平安区域,以及他们的理论作用记忆集和卡集,一个是形象一个是具体实现,在外部通过写屏障来维持援用关系的改变,介绍对于伪共享问题的解决方案并发可达性剖析当中的三色标记是一个高频“考点”,以及Hotspot是如何应答对象隐没问题的。可达性算法 在介绍具体的内容之前,这里先补充一下根底内容:什么是可达性算法呢?简略来讲实质就是判断对象是否已死?个别实现的形式有上面这几种: 援用计数法 实现的形式和原理非常简略,同样也非常的高效,就是当为每一个对象绑定一个援用计数器,当对象存活,则援用计数器+1,援用生效,则计数器-1,尽管这个计数器要耗费肯定的空间,然而的确是效率非常高的形式。当然他的毛病也非常显著,如果存在 循环援用,会导致对象永远不能断定为死亡。 循环援用:A援用B,B援用C,C援用D,D援用AJAVA固定作为GC Root的断定条件 这里单纯作为笔记进行记录: 虚拟机栈援用的对象办法区的动态属性援用对象,也就是static对象属性办法区的常量援用,比方final援用的动态常量本地办法栈的JNI,即Native办法援用对象虚拟机外部的,根本类型对应的Class对象,常驻异样对象Null...等同步锁(Syncronized)持有对象根节点枚举介绍 在可达性算法当中是通过GC ROOT的援用找到存活对象的形式,在古代的收集器根本能够做到和用户线程一起并发执行的水平,然而根节点枚举要保障某个工夫点的“快照”,这也意味着根节点枚举须要暂停用户线程。 OopMap数据结构 在HotSpot中应用的是OopMap的构造,用于存储对象的类型,或者存储特定地位记录栈外面的寄存器哪些地位是援用,垃圾收集器扫描的时候就能够间接从对应的地位开始,不须要大范畴的扫描动作。 这种构造会存在哪些问题? 这里能够看到,如果每一次对象的读取变动,都须要往OopMap外面存储内容,会导致OopMap的内容一直臃肿扩充,垃圾收集器的扫描老本会变得十分的低廉。 为了应答这一类问题,HotSpot引入了“平安点这一机制进行解决” 平安点 OopMap不会在任意的地位都收集相干的指令,而是应用一个平安点的货色,这个平安点用艰深的话了解就是高速上的“免费点”,而设置平安点的条件是:是否具备程序长时间运行特色。 平安点有什么用? 毫无疑问,平安点是为了加重OopMap存储构造的压力,同时保障垃圾回收的时候不须要扫描过多的GC ROOT。 毛病:这也决定了JVM虚拟机不能在任意的地位进行垃圾收集,而是要进入事后设定的“收费站”进行垃圾回收如何触发平安点? 实现形式有两种:抢断式中断和主动式中断 须要留神的是古代曾经没有虚拟机应用“领先式中断”暂停线程来响应GC事件,也就意味着垃圾收集的行为都是虚拟机被动执行的,而不是通过争抢的形式解决。 平安点采纳主动式中断,当垃圾收集器须要中断线程,会事后设置标记,并且各个线程会轮询标记位,一旦达到平安点左近就中断挂起(有点像检查站告诉查看)为了保障运行的高效性,JVM将应用 内存保护陷阱的形式进行自陷中断,并且这条汇编指令精简为一条,能够大大提高轮询的效率。当线程收到自陷信号,就天然会触发线程中断了。 然而这里是存在问题的,如果线程自身存在阻塞期待,或者睡眠的状况下,平安点不可能始终期待线程中断,所以这里又引入了平安区域的概念 平安区域 平安区域的次要作用是确保安全点一段的工夫内,援用的关系不产生扭转,为了实现判断,他做了上面的事件: 判断以后线程是否进入了平安区域,如果进入了进行上面的判断 如果没有实现根节点枚举,则须要期待实现根节点枚举能力放行如果曾经实现根节点枚举,则会间接放行线程。这里你能够设想在高速上期待出站,在这个区间内你要实现节点的根枚举操作才准许放行记忆集与卡表 在理解这两个名词之前,咱们须要记住 他们的目标是解决对象跨代援用的问题,在传统的分代零碎中,存在老年代援用新生代之间的互相援用,那么JVM是如何判断哪些对象援用是生效,哪些对象援用须要存活保留呢? 记忆集(RememberedSet) 首先来看下记忆集(RememberedSet)是什么货色,在源代码的构造中他被申明为一个Object[]的数组构造,能够看到保护这种构造的代价是非常昂扬的,所以为了节俭记忆集的保护老本,存在如下的解决方案: 字长精度:准确到机器字长(处理器的寻址位数)对象精度:顾名思义,准确到一个对象卡精度:准确到一块内存区域,实现最简略的形式是一个字节数组卡表 留神卡表是记忆集的一种实现形式,切忌和记忆集一概而论,他们的关系和办法区以及永恒代或者元空间的关系相似,是一种 形象与实现的关系。 既然卡精度是针对一块内存区域,而JVM刚好又是采纳了固定分代来实现垃圾回收的,所以毫无疑问应用的是卡精度来实现。 HotSpot应用的卡精度实现恰好也是应用一个字节数组来实现,卡页是2个N次幂数,最终应用的是2的9次方也就是512长度的字节数组来构建一个卡表。 如何操作? HotSpot检测到对象存在跨代指针的时候,就会把数组的标记为1,没有就会标记位0,这个过程称为“变脏”,如果垃圾收集器开启并且扫描到以后的元素变脏,团聚放入到GC ROOT当中进行扫描。 写屏障后续的内容,请在心里记住如下的问题: 卡表如何保护?写屏障的伪共享问题谁来让元素变脏什么是写屏障如何保护整个卡表定义: 咱们晓得了卡表如何定义,并且如何进行判断的,然而咱们还不分明卡表是如何进行保护的,那么什么是写屏障呢?写屏障能够认为是虚拟机层面对于“援用字段类型”的AOP的切面,写屏障还分为写前屏障和写后屏障。这里后续在进行探讨。 作用: 写屏障的作用是:保护卡表以及让卡表变脏,并且把保护卡表的操作搁置到每一次赋值操作当中。 ...

August 10, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-如何排查分区溢出问题

深刻了解JVM - 如何排查分区溢出问题前言 这篇文章会接续上一篇对于分区溢出的案例实战内容再次补充几个OOM的案例,本文不再讲述新内容,以案例实战为主,心愿这些案例能帮忙同学们理解到更多JVM对于OOM溢出的排查套路。 概述Jetty的底层机制是如何造成间接内存溢出的?如何从景象看到代码设计缺点?线程假死应该如何解决?内存使用率过高会有那些起因?这里将通过一个案例讲述常见剖析套路。队列是如何造成JVM堆溢出的,一个简略案例介绍队列数据结构设计的重要性。前文回顾: 深刻了解JVM - 分区是如何溢出的? 案例实战Jetty的如何造成间接内存溢出的?案发现场: 首先阐明这种场景并不多见,这里也是收集到一个不错的案例,这个我的项目比拟非凡,应用的不是常见的Tomcat服务器而是应用的Jetty服务器,在我的项目上线的时候忽然遇到了一个报警,此报警的内容是某一台服务的内容忽然不能进行拜访了。 此时毫无疑问第一工夫去线上查看日志,服务器挂掉很有可能是OOM了,查看一下日志之后发现了如下的内容: 毫无疑问这是一个间接内存的溢出Direct buffer memory。上网搜寻之后咱们得悉,间接内存也叫做堆外内存,不受JVM的堆限度,而是由本机的操作系统进行间接治理。接下里咱们来看下为什么会呈现间接内存的溢出。 初步排查: 这里有必要补充一下Jetty是个什么玩意:咱们能够简略了解为是和Tomcat服务器一样是一个WEB容器,他由Eclipse基金会组织进行保护,Jetty也是应用JAVA代码写的,所以也能够像Tomcat一样间接部署,同时和Tomcat一样是一个JVM过程,在Jetty启动的时候同样会和Tomcat一样监听某一个端口,而后你也能够通过发送申请给Jetty的模式,实现MVC转发等一系列的操作。 既然间接内存会产生溢出,代码下面没有应用到和NIO或者Netty相干的API内容,也没有做间接内存的调配操作,那么通过重复排查最终能够认为是Jetty在捣鬼,至于他为什么要应用Direct Memory咱们不须要关注,这里波及一个JVM的设计缺点,上面咱们会剖析对于间接内存了解和操作形式: 对于jetty的间接内存溢出感兴趣的能够看下这一篇文章:Jetty/Howto/Prevent Memory Leaks,上面截取了一段相干内容机翻过来了: 间接字节缓冲区 另一个与 jvm 谬误相干的问题是本机内存耗尽。须要留神的症状是过程大小一直增长,但堆使用量放弃绝对稳固。本机内存能够被很多货色耗费,JIT 编译器是其中之一,而 nio ByteBuffers 是另一个。 Sun 谬误编号 **#6210541** 探讨了一个仍未解决的问题,即 jvm 自身在**某些状况下调配了一个从不进行垃圾回收的间接 ByteBuffer**,从而无效地耗费了本机内存。 Guy Korland 的博客在这里和这里探讨了这个问题。因为 JIT 编译器是本机内存的一个消费者,因而可用内存的不足可能会在 JIT 中体现为 OutOfMemory 异样,例如“线程“CompilerThread0”中的异样 java.lang.OutOfMemoryError: requests xxx bytes for ChunkPool::allocate. Out替换空间?” 默认状况下,如果配置了 nio SelectChannelConnector,Jetty 将为 io 调配和治理本人的间接 ByteBuffers 池。它还通过 DefaultServlet 设置将 MappedByteBuffers 调配给内存映射动态文件。然而,如果您禁用了这些选项中的任何一个,您可能容易受到此 jvm ByteBuffer 调配问题的影响。例如,如果您应用的是 Windows,您可能曾经禁用了 DefaultServlet 上动态文件缓存的内存映射缓冲区的应用,以防止文件锁定问题。 ...

August 6, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-分区是如何溢出的

深刻了解JVM - 分区是如何溢出的?前言 JVM运行时候区溢出学习JVM必须把握的一块内容,同时因为JVM的升级换代,JVM的外部分区也在逐步的变动,比方办法区的实现由永恒代改为了元空间这些内容都是须要把握的,这一节将会是一篇对于JVM分区的总结,同样依据两个案例来说下如何排查JVM令人头痛的OOM问题。 前文回顾: 上一期次要是对JVM调优以及工具的应用做了一个专栏的阶段总结,这里不再赘述,能够看个人主页的历史文章。 概述:用图解的形式理解哪些分区会存在分区溢出的问题。如何用代码来模拟出各个分区的溢出。用两个案例来解说分区的溢出是如何排查和解决的。分区结构图简介: 在理解分区是如何溢出之前,这里先简略画一个JVM的分区运行图: 其实这一段代码在专栏开篇曾经讲过,这里间接挪用过去,同时标记了会呈现溢出的分区,下面的图对应上面这一段代码: public class OneWeek { private static final Properties properties = new Properties(); public static void main(String[] args) throws IOException { InputStream resourceAsStream = OneWeek.class.getClassLoader().getResourceAsStream("app.properties"); properties.load(resourceAsStream); System.out.println("load properties user.name = " + properties.getProperty("user.name")); }} 代码非常简单,当然和本文没有什么关系,这里间接跳过。 咱们能够看到,容易呈现办法区溢出的中央通常是这三个:办法区,JAVA虚拟机栈和JAVA堆(精确来说是老年代溢出)。这三个分区的溢出也是日常写代码当中很容易呈现溢出的状况,结构图的最上方还有一个间接内存,因为这块空间平时可能用不上然而很容易出问题所以也放进来解说,上面一一剖析他们产生溢出会呈现什么状况: 办法区:因为古代框架广泛应用动静代理+反射,所以办法区通常会产生很多的代理对象,尽管少数状况下spring的bean都是单例的通常不会产生影响,然而遇到一些须要创立大量非单例对象状况(比方并发问题)下就很容易呈现办法区的溢出。 虚拟机栈:这里看到下面的结构图可能会想1M是不是也太小了?其实每一个调配1M对于绝大多数状况下齐全够用了,让虚拟机栈溢出也比较简单, 那就是死循环或者有限递归,下文会用代码进行演示。 堆:用的最多的分区也是最容易出问题的一个分区,堆内存须要配合垃圾收集器一起进行工作,通常状况下堆溢出是因为老年代回收之后还是有很多对象(占满),导致对象无奈再持续调配而产生OOM的异样。 间接内存:因为篇幅无限间接内存是什么东东请同学们自行百度,这一块空间少数和Netty以及NIO等工具打交道的时候会有机会应用到,在这里重点解释下这块区域怎么溢出,只有记住当JVM申请不到足够的Direct Memory的时候就会造成间接内存的溢出。 会产生溢出的分区都曾经被咱们找进去了,上面就来介绍一下各自的分区是如何用代码来模仿溢出的。 分区溢出模仿:办法区: 首先是办法区的空间溢出,这里不介绍过多的概念,上一节也提到了办法区少数状况下是因为动静生成类过多导致办法区产生了溢出,上面用一段代码来模仿: 建设我的项目的步骤这里省略,间接应用IDE简略生成一个Maven的我的项目即可,首先咱们再Pom.xml文件中导入CGLIB的依赖,不分明这个CGLIB是什么也没关系,只有简略了解为能够帮忙咱们生产动静JAVA类的工具即可,即能够不应用手动new的模式实现一个对象的构建: <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version></dependency> 上面是具体的测试代码: public class CglibTest { static class Man { public void run() { System.out.println("走路中。。。。。"); } } public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Man.class); enhancer.setUseCache(true); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if(method.getName().equalsIgnoreCase("run")){ System.out.println("遇到红绿灯,期待....."); return methodProxy.invokeSuper(o, objects); } return methodProxy.invokeSuper(o, objects); } }); Man man = (Man) enhancer.create(); man.run(); } }} 这里简略解读一下代码: ...

August 5, 2021 · 2 min · jiezi

关于jvm调优:深入理解JVM-阶段总结与回顾二

深刻了解JVM - 阶段总结与回顾(二) 前言 上一文为:深刻了解JVM - 阶段总结与回顾(一),阶段总结一次要内容为JVM的一些调优常识储备以及后面的文章回顾,这一节将会总结一些JVM常见的调优套路,帮忙大家在理论状况中遇到问题的时候不用慌乱,这里要特别强调一点:平时写代码要常常回顾和逻辑梳理,对于一些不确定的办法肯定要点开源码进行解读。 之前有评论说思维导图更直观一些,所以后续的文章都会尽量附上思维导图节俭大家的工夫。 前文回顾: 上一节咱们给了两个简略的案例,解说了FULL GC的常见排查套路,依据排查套路能够帮忙咱们疾速找到排查的方向并且及时的定位到真正的问题点,这对于排查线上问题是一项很重要的技能。 另外咱们还讲述了String.split()是如何让JVM频繁进行FULL GC的,咱们用图表一步步剖析出基本后果,并且解读源码剖析了这个办法在JDK版本升级的改变,因为降级后源代码在切割字符串之后创立SubList对象(底层为数组)导致这一问题的产生,这里JDK要背一部分锅,因为它违反了 向下兼容这一规定,然而更多状况下是因为开发人员对于办法理解水平不够,最终导致代码逻辑产生内存透露! 以上就是上一节的所有内容。 阶段总结:在个人主页也有历史文章,欢送来踩 深刻了解JVM - 解读GC日志 次要内容还是以解说如何浏览日志,同时不同的机器运行的后果不同,文章更多的是介绍如何解读参数。 深刻了解JVM - 实战JVM工具(上) 这篇文章次要介绍一下罕用的JVM工具,当然介绍这些工具是没有意义的,因为不去应用吃个饭根本就会忘光,所以这篇文章次要为应用工具实操一下大抵如何监控和调优代码。 深刻了解JVM - 实战JVM工具(下)介绍三个JVM调优的案例,一步一步剖析问题和解决办法。总结剖析思路和解决流程,自我思考和反思。总结和集体感想。深刻了解JVM - 案例实战排查Full Gc的套路是什么,这里用一个电商案例来进行阐明。spilt()办法是如何造成内存泄露的?如何通过可视化图形剖析出问题。以及如何从源代码层面发现基本问题思维导图: 幕布地址:https://www.mubucm.com/doc/Ig... 概述第二阶段的文章回顾和总结你真的相熟老年代么?对象在什么时候会进入到老年代?频繁的FULL GC通常有什么起因优化JVM须要留神哪些点?你真的相熟老年代回收么? 传统的JVM模型采纳固定分代的模式,首先咱们来回顾一下老年代的回收触发条件。 对象什么时候进入老年代 新生代是如何回收内存的? 讲述老年代回收之前,咱们回顾一下新生代是如何分配内存的,没错,就是采纳的 改进复制算法,新生代应用的是eden+2个survior区域进行内存的布局,默认状况下是8:1:1,这个值是能够扭转的,咱们能够应用参数:-XX:SurvivorRatio=8进行扭转,最初咱们来看下他的结构图: 改进复制算法在eden区域占满之后,触发一次YGC,会把存活对象复制到S1区域,之后清空掉整个eden区,这个过程是十分快的,而到了下一次YGC,会把S1中的存活对象和Eden区域的存活对象复制到S2区域,并且清空掉S1区域和EDEN区域。所以新生代回收的特点是:内存分为三块区域,每次只应用其中的两块,留存一块作为备用切换 以上是新生代的回收流程。当新生代触发YGC的时候,存活对象依据动静判断条件会进入老年代,那么老年代的FULL GC是如何解决的?他的触发条件是什么?这里的内容要重复回顾,因为非常的重要: 一个对象躲过了15次垃圾回收,年龄一到就进入老年代对象超过新生代的总大小,超过肯定的阈值,会间接往老年代调配一次Young GC之后存活对象太多了,因为Survior区域无奈寄存,这批对象间接进入老年代对象进入到Survior区域之后,Survior忽然发现对象的占用内容超过50%,此时会依据年龄排序,把大于Survior区域的50%的年龄N的对象进入老年代,比方年龄2,3,4的对象,年龄为3的对象占比超过50%,意味着年龄3、4会进入老年代。老年代GC是如何触发的? 老年代的FULL GC如何触发呢,在之前的文章有表格讲述触发老年代的工夫,这里再次总结一下: 在CMS收集器,老年代本身有一个阈值,在JDK6之后默认是占满老年代空间92%之后将会进行触发。然而须要留神的是这个百分比不是固定的,在JVM中会依据理论的老年代占用状况提前完成垃圾回收。YGC之前,会先判断老年代最大间断可用内存空间大小是否大于新生代历次进入老年代的均匀大小,如果不符合要求会在YGC之前触发一次FULL GC,回收掉一部分老年代对象,而后执行YGC。如果YGC存活对象太多,Survior区域放不下,如果要放入老年代,要是此时老年代也放不下,就会触发Full GC,回收老年代一批对象,再把这些年老代的存活对象放入老年代中。失常状况下一班多少次GC: 失常状况下FULL GC的频率在几十分钟一次,几小时一次,这个GC频率还算是比拟能够承受的,当然每次FULL GC最好在几百毫秒内。 如果线上的零碎有这个体现根本不须要太关怀优化的问题。 如何察看JVM内存模型:剖析手法:这里能够依照上面的过程进行剖析: EDEN区域的对象增长速率有多快?YGC的频率有多高一次YGC多长的耗时老年代增长的速率多高Full GC频率多高一次Full GC耗时频繁FULL GC的几种体现:机器CPU负载高频繁FULL GC报警零碎无奈解决申请或者过慢。频繁FULL GC个别有哪些起因: 之前讲述的案例比拟多,这里集中解说以下: ...

August 4, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-实战JVM工具下

深刻了解JVM - 实战JVM工具(下)前言 接着上篇持续讲述,上一篇模仿了两个还算比拟相熟的场景,剖析了之前老年代优化是如何解决的,以及应用jstat剖析工具如何剖析出JVM的问题,这一节会持续扩大,将会列举更多的案例来分析线上的JVM问题。 前文回顾 上一节通过一个APP的JVM内存剖析解释了一些比拟非凡的参数如何影响JVM,以及剖析了之前老年代优化的文章中对于jstat如何进行剖析和优化。 概述:介绍三个JVM调优的案例,一步一步剖析问题和解决办法。总结剖析思路和解决流程,自我思考和反思。总结和集体感想。案例实战案例1:办法区一直解体如何排查?业务场景: 之前的案例根本都是和堆扯上关系,这个案例比拟非凡是由 JVM参数设置谬误引起的频繁的卡顿问题,简略来说就是设置了参数之后就呈现拜访零碎卡顿并且线上一直的进行FULL GC的报警,这里先不阐明改了哪一个参数,而是先来剖析一下: 发现零碎在十分钟内进行了3次FULL GC,这个频繁非常高通过线上的JSTAT排查发现呈现了报错Methoddata GC Threashold 等字样 依据第二点咱们初步判定是JVM的办法区溢出了,为什么JVM的办法区溢出会触发FULL GC?事实上的确如此,因为通常状况下FULL GC也会带动办法区的回收。这一块的材料网上能够搜到一大堆,这里不再具体的介绍。 问题剖析:退出验证参数剖析: 既然是办法区的问题,为了进一步的排查这个办法区溢出的问题,这里须要退出上面的两个参数: -XX:TraceClassLoading-XX:TraceClassUnloading 这两个参数的作用是追踪类加载和类卸载的状况,在加上这两个参数之后持续剖析,之后发现在日志的文件当中发现了上面的内容: 显著能够看到JVM一直的加载了一个叫做GeneratedSerializationCOnstructorAccessor的类,就是这个类一直的加载导致了metaspace区域占满,这也导致了metaspace的对象太多触发FULL GC,所以罪魁祸首就是这个奇怪的类GeneratedSerializationCOnstructorAccessor。 为什么会呈现奇怪的类? 这里应用google搜寻看看这个类是什么,查问后果发现这是JDK内置类,通过查阅材料咱们能够晓得这是由反射生成的类。 反射的知识点也不再补充,能够了解为一种通过JVM的类加载器联合JVM的工具包生成建设对象的一种形式,也是许多框架的灵魂ss。 其实通过材料查阅咱们还能够发现 反射须要JVM动静的生产一些下面所说的奇怪的类到MetaSpace区域,比方要生成动静类ProxyClass@Proxy123123等等相似这种对象(反射生产的类标识比拟非凡),JDK都须要下面的辅助对象进行操作。 这里咱们还须要在理解一个概念,就是反射生成的类都是应用的软援用!至于这个软援用在这里产生了什么影响,这里也先卖个关子,到下文联合把零碎搞解体的参数一起进行剖析。 什么是软援用?在内存空间有余的时候被强制回收,不论是否存在局部变量援用。 接着剖析对于软援用的存活工夫,jvm应用了上面的公式来计算这个软援用的生命周期: 这个公式的含意:示意一个软援用有多久没有被拜访过了,freespace代表了以后的JVM中的闲暇内存,softref 代表每一个MB的空间内存空间能够容许SoftReference 存活多久。 估算值:假如当初空间有3000M的对象,softrefLRUpolicyMSPerMB的值为1000毫秒,意味着这些对象会存活 3000秒 也就是50分钟左右。 以上就是奇怪的类呈现的起因,是因为反射惹的祸。 排查后果:到底设置了什么参数? 这里讲一下到底设置了参数,让反射一直生成对象把办法区占满了,这里设置的参数如下: -XX:SoftRefLRUPolicyMSPerMB=0这个参数。后果JVM就翻车了。 为什么呈现奇怪的对象越来越多? 咱们再看一下下面的公式: 假如咱们不小心把这个值设置为0有什么结果呢? 当然是会导致下面clock公式的计算结果为0的,后果就是JVM发现每次反射调配的对象马上就会被回收掉,而后接着又会通过代理生成代理对象,简略来说这个参数导致每次soft软援用的对象一旦调配就会马上被回收. 再次强调一下反射机制导致动静代理类一直的被新增,然而这部分对象又被马上回收掉,导致办法区的垃圾对象越来越多这就是为什么奇怪的对象越来越多的起因。 为什么会想着设置这个参数? 设置这个参数的起因也很天真:为了让反射生成的代理对象能够尽快被垃圾回收,如果设置为为0,当办法区的内存占用能够小一些,并且也能够及时回收,而后后果就是善意办好事。 解决办法: 这里解决办法很简略,就是设置一个大于0的值并且最好是1000、2000、5000这一类数字,就是不能设置的过小或者设置为0,否则会导致办法区一直的占用后果办法去溢出最终又导致FULL GC。 总结: 这个案例可能看下面的阐明很简略就解决了,然而实际上真正碰到相似问题,必定会呈现各种摸不着头脑的状况,心愿这篇案例能够让读者设置JVM参数的时候都要验证一下这个参数的影响以及肯定要确认他的参数和实际效果是统一的! ...

July 31, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-G1收集器

深刻了解JVM - G1收集器前言 上一篇通过案例阐明了老年代的常见优化和解决形式,这一节来看下目前最为热门的G1收集器,G1收集器也是JDK9服务端默认的垃圾收集器,尽管JDK9在当初看来还不是非常的遍及,然而学习这个垃圾收集器是非常重要也是十分必要的。 前文回顾 上一节咱们通过一个电商的模仿实战,理解了老年代常见的优化形式。同时在最初总结了老年代优化的一些常见的套路,上面间接用上一节的总结回顾整个内容: - 首先业务的对象都是生命周期非常短暂的对象,新生代的压力比老年代要大,所以适当放大老年代空间是非常划算的- 预测在高并发的场景下对象进入老年代的机会,如果对象常常“跨区”阐明有一部分内容空间是节约了,那就是Survior区域- 对象在各分区须要大抵多少的内存空间,比方每个线程须要占用多少的内存空间- 对象的年龄判断是否须要改变,提前让对象进入老年代是益处还是害处- 关注收集器对于对象垃圾回收的影响,同时在启动的时候要强制应用某一垃圾收集器,因为不同的JDK版本默认的垃圾收集器是不一样的。CMS+ParNew收集器的痛点是什么? 在之前的文章咱们提到过,传统的黄金组合的痛点Stop world (世界进展):在并发标记和并发整顿阶段如果呈现调配对象超过老年代代而导致Full Gc,会立即停下手头的所有工作全力进行垃圾收集的动作,并且应用Serial old收集器解决,这对于用户来说会发现显著的卡顿,同时会误认为零碎卡死,体验非常差。 G1收集器的介绍为什么会诞生G1? 感兴趣能够看下对于G1论文的白皮书:http://citeseerx.ist.psu.edu/...。(看评论说其实讲的非常形象和抽象,倡议深入研究G1的大抵看看) 为什么会诞生G1呢?最大起因是JVM目前不能让收集器的垃圾回收进展在某个特定工夫范畴,然而也不须要像实时那样要求非常的严格。基于这样的思考,早在06年就呈现了G1的论文,然而最终实现花了好多年的工夫!能够设想这个收集器外部注定是非常复杂的,当然咱们没有必要去深入研究底层,只须要他的根底原理即可。 历史进程:Jdk7 update4被商用jdk8 update40 并发类型卸载反对Jdk9成为服务端默认垃圾收集器Jdk10因为cms垃圾收集器插件性能耦合等缘故,重构了“对立垃圾回收接口”G1收集器的特点设置一个垃圾的预期进展工夫。依据Region的大小和回收价值进行最有效率的回收。内存不再固定划分新生代和老年代,应用Region对于内存进行分块,实现了依据系统资源动静分代。Region可能属于新生代或者老年代,同时调配给新生代还是老年代是由G1本人管制的。抉择最小回收工夫以及最多回收对象的region进行垃圾的回收操作。补充《深刻了解JVM虚拟机》介绍的特点: 不再保持固定大小以及固定数量的分区划分应用region划分区域,大小相等,不同区域表演不同的角色humongous区域:设计被用于大对象应用,依据最短进展工夫模型优先回收价值最高的region。超过一个region一半的大小都为大对象后盾应用一个优先级列表优先回收收益最大的区域G1的Region G1勾销了固定分代的概念,取而代之的是应用分区的概念,把内存切分成一个个小块,同时各个小块又分为新生代(eden、Survior区)、老年代、大对象(humongous区)等等。 G1规定大于Region的一半的对象成为大对象,同时不参加分代。 Region并不是固定为新生代或者老年代,通常状况为自在状态,只有在须要的时候会被划分为指定的分代并且寄存特定对象。这里兴许会有疑难,这样调配Region不会产生很多垃圾碎片么?答案当然是并不会,首先新生代应用复制算法,会把存活对象拷贝到一块region进行寄存,同时存活对象大于肯定的region占比不会进行复制(下文会提到),把整个region清理并且进行回收,而老年代则应用标记-整顿算法,同样的情理也会间接把垃圾对象的region给清理掉,也是不会产生内存碎片的,最初,大对象是横跨多个region并由专门的region寄存,一旦回收间接干掉大对象把对应的region清理即可。 到底有多少region,每个region的大小是多少? 计算形式: 堆大小 / 2048,G1收集器最多能够有2048个region,并且大小必须是2的倍数。也就是说4G内存给每一个region的大小是2M的大小。 上面是G1常见的配置参数: -XX:Heap Region Size:手动制订region的大小。单个region的大小指定,默认为2M,4g内存当中-XX:G1NewSizePercent: 手动指定新生代初始占比,默认是5%-XX:G1MaxNewSizePercent:新生代的最大占比默认是60%罕用参数:-XX:G1MixedGCCountTarget:示意一次垃圾回收之后,最初一次混合回收执行几次。这个参数意味着在垃圾回收的最初一个阶段回收和零碎运行交替运行,并且默认回收8次之后完结这个操作。-XX:G1HeapWastePercent:默认为5%。在混合回收的时候如果一次混合回收的闲暇region超过5%就立即进行垃圾回收的操作,这个参数也是为了尽量减少进展的工夫设计的。-XX:G1MixedGCLiveThreasholdPercent:默认值为85%,示意存活对象要低于85%才进行回收的操作。因为如果一个region的存活对象大于85%,复制拷贝的代价是非常大的,并且这一类region大概率进入老年代-XX:G1MaxNewSizePercent:新生代最大占用堆内存空间,默认值为60%。这个参数意味着在初始5%的新生代状况下,零碎运行最多能够从零碎总空间调配60%给新生代应用,超过这个值就必定会触发新生代回收。-XX:NewRatio=n:配置新生代与老年代的比例,默认是2:1如何了解G1的工作模式? 为了更好了解G1的工作模式进展工夫模型,这里集体想了一个还算活泼的例子解释下G1的工作模式: 咱们平时去服务比拟周到的餐馆,服务员通常会看咱们桌子上垃圾的量有多少或者吃剩的盘子有多少,当垃圾超过一定量之后,服务员就会过去开盘子或者收垃圾,而后不便前面上菜,同时也能够让洗盘子的服务员忙忙停停的工作。 这之后就会思考,如果每多出一个盘子就去收一个,你会不会很累,会不会等到盘子有一定量了再去收,比方桌子摆不下新的菜了,或者盘子叠了很多个。 最初,如果每次开盘子的速度能赶上上菜的速度,那么根本的失常运行是没有大问题的。 G1适宜什么样的零碎? G1适宜的零碎包含超大内存的零碎,此时如果依照传统的分代,比方16G的内存新生代8G,老年代8G这种划分,尽管拉长垃圾进展的工夫,然而一旦新生代被占满,回收的效率是非常低的,因为对象和GC ROOT十分多,最终导致垃圾收集长时间卡顿。而G1不一样,他只须要依照算法判断依据进展模型的值在新生代靠近进展模式工夫的时候就马上开启回收,不必等新生代满了才回收。 G1也适宜须要低提早的零碎,因为低提早对于零碎的响应要求是十分高的,更加看重响应的工夫,同时对于零碎的资源要求也比拟高,而分代的模型和实践在大内存机器会造成长时间的垃圾收集进展,这对于实时响应的服务要求是十分高的。 G1比照Parnew+cms最大的提高在哪里? G1最大的提高也是他的特点就是 工夫进展模型, 能够管制stop world的工夫。同时能够让垃圾收集的工夫管制在预期设置的范畴之内。 其余提高: 算法: G1基于标记-整顿算法, 不会产生空间碎片,调配大对象时不会无奈失去间断的空间而提前触发一次FULL GC。多线程:多线程算法比CMS更加优良,同时更能施展多核性能G1没有毛病了? 没毛病是不可能的,必定是有毛病,这个收集器最大的问题恰好在于进展模型,外面的算法细节非常的简单,所以咱们调试不能仅仅通过业务推算,而是要依据日志以及工具辅助来实现调优,G1的调优是十分麻烦的一件事。 其次就是Region的设计了,region的设计自身要耗费大量的系统资源进行保护的,这意味着G1收集器自身至多须要占用10%-20%的内存来维持本人的失常工作,比方计算进展模型,维持跨代援用和GC ROOT等信息,这两头蕴藏的细节十分复杂,这里的内容能够参考【其余材料-干货文章】这部分理解,所以G1收集器自身就须要较好的机器性能才倡议应用。 ...

July 21, 2021 · 1 min · jiezi

关于jvm调优:深入理解JVM-实战老年代优化

深刻了解JVM - 实战老年代优化前言 通过后面的文章能够理解到JVM优化中老年代的FULL GC对于零碎以及垃圾收集器的行为有着非常大的影响,比方CMS并发标记或者回收撑不住的时候要暂停用户线程并且呼叫serrial收集器帮忙进行单线程的高效回收的动作,然而也随同着"漫长"的stop world工夫。 综上所述,老年代的优化是JVM优化的一个外围知识点,所以这一节就来解说如何优化老年代的回收,尽量让对象在新生代回收而不是在老年代进行回收。 前文回顾 之前的文章咱们对于JVM分代以及垃圾回收有了一个具体的理解,同时理解了jdk9之前支流的垃圾收集器ParNew收集器和CMS收集器,并且对于cms的细节进行了残缺的讲述,这些内容都是非常根底然而又非常重要的知识点,这篇文章则依据后面的知识点,依据一个模仿的案例来解说一下如何对于JVM老年代进行调优。 案例实战 案例实战会依据一个十分理想化的模仿场景,因为利用的性能实际上会有各种的影响,甚至代码的品质也会影响零碎的运行性能,所以上面的模仿场景均为假想的状况,切勿过于认真的看待这个案例的各种参数。 一个电商零碎的大抵背景: 如果一个电商网站每天的访问量是20次/人,如果要上亿次的申请须要每天500万次的申请,同时如果这500万人依照10%的下单的规范,则是每天50万人会进行下单的操作,而下单操作依照2/8准则在4小时之内付款实现,那么此时的占用大略是50万/4小时 == 500000 / 14400,大略每秒也就 34个订单左右,这种状况下发现零碎的影响并不会很大,老年代产生回收大略为几个小时一次,齐全能够承受。 高并发的场景 然而如果在秒杀的场景,状况又不一样了,如果在一秒内来1000笔订单,该如何解决?咱们假如如果是3台机器,则每台须要解决至多300条申请。 计算JVM耗费 依据上文模仿场景,假如每秒300个申请依照每个对象1KB来看,每一台机器要解决大略300KB的内存,把一个订单零碎的解决对象放大10倍,则是3000KB,如果在算上其余的操作比方订单解决,则须要30000KB = 30MB的占用。 如果虚拟机栈每个占用1M,则几百个线程须要几百M的空间。如果是4外围8G的机器,则分4G给JVM,4G中分1G给虚拟机栈500M多M,办法区:256M,堆外内存给256M。同时开启内存担保机制(jdk6之后不须要制订参数)而后新生代和老年代各调配1.5G。 依照下面的换算,咱们发现如果每秒都来30M对象,那么1200M左右的EDEN区域(8:1:1的比例大略是1200给EDEN),大略会留下200M左右的内存会进入SURVIOR区域,然而如果SURVIOR区域放不下则会进入老年代,依据之前的参数调配150M的Survior区域必定是无奈寄存的,依据内存调配担保的机制,这些对象会调配到老年代。 依照这样的调配效率不到一分钟新生代就会塞满。大略8、9次minor gc就会导致full gc,也就是说 8、9分钟就会触发老年代回收,这个触发的概率就非常高了,这会重大导致系统卡顿并且呈现用户线程的进展景象。 然而如果Survior空间足够,那么此时回收进入到Survior空间之后,在下一次minor gc根本也为垃圾对象被回收了。 垃圾回收的优化 下面的案例优化也非常简单,在解说最终的优化计划之前,咱们依照上面的步骤进行剖析:查看Survior区域是否能够保障每次minor gc都能够全副进入 首先咱们须要确认新生代的内存在垃圾回收之后是否都能够进入到Survior区域,很显著,依据案例Survior区域的大小为150M左右,而每次Minor GC之后存活对象通常为200M左右,这时候显著是无奈寄存下的,所以咱们须要。 多大的对象应该进入老年代 之前说过一秒钟会产生30M对象,而Minor gc之后对象根本只剩下100M左右了,也就是说1分钟大略存活100M对象,那么平摊下来一秒大略产生1-2M的大对象。 所以个别状况下设置个1M的阈值就差不多了 对象通过多少年龄进入老年代 个别状况下默认的15就是一个不错的值。然而对于高并发的业务来说,大对象早点进入老年代反而是坏事。因为survior存在一个管制值50%,累加对象大小超过Survior区域50%之后大于等于此年龄全副会进入老年代,所以有时候让新生代的对象提前进入老年代也是一种值得思考的事件。 比方咱们能够将进入老年代年龄的对象设置为7或者8。 指定垃圾收集器 留神肯定要在参数外面指定垃圾收集器,这是非常重要的内容。 比方:**-XX:+UseParNewGC**最终优化参数后果 通过下面的一系列剖析,咱们能够确定基本问题出在了对象提前进入了老年代导致Survior区域成为陈设并且老年代的对象一直扩大,最终老年代塞满而导致频繁full gc,所以案例最初的优化参数如下: 老年代的内存要如何优化呢? 针对下面的案例,咱们再剖析几点内容: 老年代须要开启调配担保失败么? 咱们看下如果没有开启调配担保失败会如何?首先如果没有开启,如果此时老年代的可用内存为400M,并且发现新生代总大小 < 老年代可用内存大小,每次Minor Gc都将会随同着Full Gc,所以jdk6也敞开了这个参数并且这个参数也是默认开启的,绝大多数的状况下这个参数不必去管,默认都是要开启的。 如果老年代的总大小<新生代的大小,那么如果没有开启调配担保每一次申请都是会产生Full Gc的。 ...

July 15, 2021 · 1 min · jiezi

关于jvm调优:生产环境-OOM-与-GC-问题的处理思路

有肯定 Java 工作教训的敌人们,免不了要遇到过,或者解决过 OOM 和 GC 问题。OOM 和 GC 问题也是面试时,常常被面试官问题的问题。分享一下多年积攒的一些小教训,共同进步。 0x01:防患未然部署到生产环境的利用,无论是 C/S 构造,还是 B/S 构造的应用服务。必定有基于 Shell 脚本编写的启动脚本。C/S 构造的应用服务的 Shell 脚本个别是公司外部开发人员编写的;以下一个 C/S 构造应用服务的简略启动脚本。 java -Xms1024m -Xmx1024m -XX:PermSize=256m \-XX:MaxPermSize=512m -XX:-HeapDumpOnOutOfMemoryError \-XX:HeapDumpPath=./ -XX:+PrintGCDetails -Xloggc:./gc.log -jar \plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar -clean -refresh &而 B/S 构造的个别是应用服务器的启动 Shell 脚本。例如,应用服务器 Apache Tomcat bin 目录下的 startup.sh。 而 Apache Tomcat 的启动 Shell 脚本并没有配置产生 OOM 时,打印 JVM 内存快照的JVM参数和打印 GC 日志的JVM参数。所以生成环境的 Tomcat 服务个别须要进行 JVM 参数优化。 怎么对线上的 OOM 和 GC 问题进行防患未然呢?那就是认为本人部署的任何服务都是会产生 OOM 和 GC 问题的。在启动脚本里加上相应的参数,避免真的呈现 OOM 和 GC 问题时,无证可查。 ...

March 15, 2021 · 2 min · jiezi

关于jvm调优:面试之加分项JVM-类加载机制

当 Java 虚拟机将 Java 源码编译为字节码之后,虚拟机便能够将字节码读取进内存,从而进行解析、运行等整个过程.。 这个过程咱们叫:Java 虚拟机的类加载机制。JVM 虚拟机执行 class 字节码的过程能够分为七个阶段:加载、验证、筹备、解析、初始化、应用、卸载。 在开始聊之前,先给大家看一道面试题。 class Grandpa{ static { System.out.println("爷爷在动态代码块"); }} class Father extends Grandpa{ static { System.out.println("爸爸在动态代码块"); } public static int factor = 25; public Father() { System.out.println("我是爸爸~"); }}class Son extends Father{ static { System.out.println("儿子在动态代码块"); } public Son() { System.out.println("我是儿子~"); }}public class InitializationDemo{ public static void main(String[] args) { System.out.println("爸爸的岁数:" + Son.factor); //入口 }}请写出最初的输入字符串。 正确答案是: 爷爷在动态代码块爸爸在动态代码块爸爸的岁数:25我置信很多同学看到这个题目之后,表情是解体的,齐全不晓得从何动手。有的甚至遇到了几次,依然无奈找到正确的解答思路。其实这种面试题考查的就是你对Java类加载机制的了解。如果你对Java加载机制不了解,那么你是无奈解答这道题目的。这篇文章,我将通过对Java类加载机制的解说,让你把握解答此类题目的办法。 加载上面是对于加载过程最为官网的形容。加载阶段是类加载过程的第一个阶段。在这个阶段,JVM 的次要目标是将字节码从各个地位(网络、磁盘等)转化为二进制字节流加载到内存中,接着会为这个类在 JVM 的办法区创立一个对应的 Class 对象,这个 Class 对象就是这个类各种数据的拜访入口。 ...

December 23, 2020 · 3 min · jiezi

关于jvm调优:内存迟迟下不去可能你就差一个GCCollect

背景咱们有一家top级的淘品牌店铺,为了后续的减速计算,在程序启动的时候灌入她家的外围数据到内存中,灌入实现后内存高达100G,尽管云上的机器内存有256G,然被这么划掉一半看着还是有一点疼爱的,可怜那些被挤压的小啰啰程序????????????,本认为是那些List,HashSet,Dictionary须要动静扩容虚占了很多内存,也就没当一回事,起初过了一天发现内存回到了大略70多G,卧槽,不是所谓的汇合虚占,而是GC没给我回收呀。 windbg验证一下为了验证我的说法,我就不去生产抓这个硕大无朋的dump了,去测试环境给大家抓一个,早晨清蒸。 !eeheap -gc 查看gc信息0:000> !eeheap -gcNumber of GC Heaps: 1generation 0 starts at 0x0000019b0fc66b48generation 1 starts at 0x0000019b0f73b138generation 2 starts at 0x0000019a5da81000ephemeral segment allocation context: none segment begin allocated size0000019a5da80000 0000019a5da81000 0000019a6da7ffb8 0xfffefb8(268431288)0000019a00000000 0000019a00001000 0000019a0ffffe90 0xfffee90(268430992)0000019a10000000 0000019a10001000 0000019a1ffffeb0 0xfffeeb0(268431024)0000019a20000000 0000019a20001000 0000019a2fffffb0 0xfffefb0(268431280)0000019a30000000 0000019a30001000 0000019a3ffffc50 0xfffec50(268430416)0000019a40000000 0000019a40001000 0000019a4fffffc8 0xfffefc8(268431304)0000019a7aad0000 0000019a7aad1000 0000019a8aacfd60 0xfffed60(268430688)0000019a8cbf0000 0000019a8cbf1000 0000019a9cbefe10 0xfffee10(268430864)0000019a9cbf0000 0000019a9cbf1000 0000019aacbefcb8 0xfffecb8(268430520)0000019aacbf0000 0000019aacbf1000 0000019abcbefd18 0xfffed18(268430616)0000019abcbf0000 0000019abcbf1000 0000019accbefd68 0xfffed68(268430696)0000019accbf0000 0000019accbf1000 0000019adcbefcf8 0xfffecf8(268430584)0000019adcbf0000 0000019adcbf1000 0000019aecbefdc0 0xfffedc0(268430784)0000019af0e20000 0000019af0e21000 0000019b00e1ff28 0xfffef28(268431144)0000019b00e20000 0000019b00e21000 0000019b10047178 0xf226178(253911416)Large object heap starts at 0x0000019a6da81000 segment begin allocated size0000019a6da80000 0000019a6da81000 0000019a756d0480 0x7c4f480(130348160)0000019b10e20000 0000019b10e21000 0000019b133ca330 0x25a9330(39490352)Total Size: Size: 0xf940ee70 (4181782128) bytes.------------------------------GC Heap Size: Size: 0xf940ee70 (4181782128) bytes. 从最初一行能够看到堆大小: GC Heap Size: Size: 0xf940ee70 (4181782128) bytes. 而后将4181782128 byte 转化为GB: 4181782128/1024/1024/1024= 3.89G。 ...

October 29, 2020 · 4 min · jiezi

关于jvm调优:JVM频繁GC分析

本文记录一次频繁GC的剖析 问题查看我的项目日志发现GC频繁,简直几秒钟一次 查看GC日志[GC (Allocation Failure) [PSYoungGen: 6816K->320K(8192K)] 82693K->76229K(187904K), 0.0032930 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 6464K->224K(8192K)] 82373K->76165K(187904K), 0.0067897 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 6368K->192K(8192K)] 82309K->76149K(187904K), 0.0052315 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 6336K->1152K(8192K)] 82293K->77117K(187904K), 0.0026567 secs] [Times: user=0.05 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 7296K->1184K(8192K)] 83261K->77157K(187904K), 0.0049316 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 7202K->1056K(8192K)] 83175K->77037K(187904K), 0.0053079 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 7181K->912K(8192K)] 83162K->76901K(187904K), 0.0045949 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 7056K->544K(8192K)] 83045K->76557K(187904K), 0.0066989 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] GC日志打印YGC日志十分频繁,几秒钟一次。PSYoungGen 是应用Parallel Scavenage垃圾收集器的年老代从日志上能够看到 7056K->544K(8192K) 年老代总大小十分小只有8M,这就是产生频繁YGC的起因,当new了一个新对象,Young区内存不够调配时,就会触发一次YGC。那么为什么Young区会这么小呢? ...

October 12, 2020 · 2 min · jiezi

关于jvm调优:面试必问亿级流量优化策略之JVM调优文档视频面试还不收藏

很多程序员感觉性能调优这块的JVM、Mysql不是什么小事,本人平时写代码写得好好的,不是很理解JVM如同也没什么的,认为得千万级甚至亿万级的大流量、大我的项目才用得上,其余个别场景基本用不到,直到遇见这样的场景—— 你是否经验过这样的场景 线上零碎CPU、IO、内存忽然被打满,接口响应工夫过长线上零碎忽然卡死无法访问,频繁收到GC报警 线上零碎忽然内存溢出OOM,内存泄露无奈定位 线上生产环境不晓得如何设置JVM各种参数 线上零碎SQL执行迟缓导致系统接口超时 线上数据库Mysql并发过高导致死锁 线上数据库Mysql莫名抖动无奈定位 如果你答复不上来,更得留神了!不论我的项目规模大小,要想搞懂 Java 代码调优,都必须办法跟生产联合。正当的优化可能极大的进步工作效率。上面是很多开发者常常会遇到的问题: 网上看过调优相干知识点,但没有实操过,看完就忘;工作时 Redis、Kafka 等都是间接调用,一出问题就束手无措;因为调优仅仅是调个参数,重点是优化代码;面试官问到调优教训,后果平时基本没有实际操作过;一遇到高并发生产问题,就加机器,加配置,没有好的解决思路。这些问题不解决,往往会大概率重复 CRUD,天天熬夜加班。如果你不想始终做石破天惊的 CRUDer,如果你想在团队有担当,或者你欲望很简略——就想钱多事少离家近,那倡议你肯定要啃下性能调优。 听下来,你能够播种: 1. 彻底把握JVM最底层原理,应答大厂面试慌慌张张 具备剖析、定位与解决大型零碎生产环境JVM问题的能力彻底把握Mysql底层优化原理,横扫所有对于Mysql优化的面试题具备剖析与优化大型零碎线上环境Mysql各种性能问题的能力5.具备构建性能稳固的大型分布式系统高并发高可用线上环境的能力 那对于jvm,都有哪些须要去学习筹备的呢? 思维导图 学习文档全文没有多余的废话,一个知识点对应一个解说,当你须要学习jvm的相干知识点的时候,这会是一个很好的参考,并且,想学调优,还是须要从底层原理登程,只有明确底层的货色能力更好的调优不是吗?尽管之后不肯定能用到,然而在面试的时候,要想去一些比拟好的公司,这些又是必问的内容,你该如何抉择呢? 须要这几份材料的,相应的文章曾经整顿造成文档,git扫码获取材料看这里 —、JVM内存区域划分 二、JVM执行子系统 三.垃圾回收器和内存调配策略 四、编写高效优雅Java程序 五、性能优化 面试如果你最近短时间内有面试需要,而面试还没有筹备好,没关系,常备不懈,不快也光 面试题上 面试题下 视频当然,只有文档怎么能够,在我看来看文档太浪费时间了,来吧,视频在这里,联结大厂面试题,解说jvm以及多线程,还不快点珍藏

September 12, 2020 · 1 min · jiezi