当前位置: 首页 > news >正文

手机网站制作费用网站关键词优化快速排名

手机网站制作费用,网站关键词优化快速排名,微商城网站建设价格,广州市网站建设 合优1、调优的基本问题 1.1、为什么要调优#xff1f; 目的是防止出现OOM#xff0c;进行JVM规划和预调优#xff1b;解决程序运行中各种OOM#xff1b;以及减少Full GC出现的频率#xff0c;解决运行慢、卡顿问题。 1.2、调优的大方向 合理的编写代码#xff0c;充分并合理…1、调优的基本问题 1.1、为什么要调优 目的是防止出现OOM进行JVM规划和预调优解决程序运行中各种OOM以及减少Full GC出现的频率解决运行慢、卡顿问题。 1.2、调优的大方向 合理的编写代码充分并合理的使用硬件资源以及合理地进行JVM调优。 2、调优监控的依据 运行日志异常堆栈GC日志线程快照堆转储快照 3、性能优化的步骤 3.1、第1步熟悉业务场景 3.2、第2步发现问题性能监控 一种以非强行或者入侵方式收集或查看应用运营性能数据的活动。监控通常是指一种在生产、质量评估或者开发环境下实施的带有预防或主动性的活动。当应用相关干系人提出性能问题却没有提供足够多的线索时首先我们需要进行性能监控随后是性能分析。 监控前设置好回收器组合选定CPU主频越高越好设置年代比例设置日志参数生产环境中通常不会只设置一个日志文件。比如 -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:UseGCLogFileRotation -XX:NumberOfGCLogFiles5 -XX:GCLogFileSize20M -XX:PrintGCDetails -XX:PrintGCDateStamps -XX:PrintGCCause GC 频繁CPU Load 过高OOM内存泄漏死锁程序响应时间较长 3.3、第3步排查问题性能分析 一种以侵入方式收集运行性能数据的活动它会影响应用的呑吐量或响应性。性能分析是针对性能问题的答复结果关注的范围通常比性能监控更加集中。性能分析很少在生产环境下进行通常是在质量评估、系统测试或者开发环境下进行是性能监控之后的步骤。 打印GC日志通过GCviewer或者 http://gceasy.io来分析日志信息灵活运用命令行工具jstackjmapjinfo等dump出堆文件使用内存分析工具分析文件使用阿里Arthas或jconsoleJVisualVM来实时查看JVM状态jstack查看堆栈信息 3.4、第4步解决问题性能调优 一种为改善应用响应性或呑吐量而更改参数、源代码、属性配置的活动性能调优是在性能监控、性能分析之后的活动。 适当增加内存根据业务背景选择垃圾回收器优化代码控制内存使用增加机器分散节点压力合理设置线程池线程数量使用中间件提高程序效率比如缓存消息队列等 3.5、常用方式 设置必要的内存参数。比如 -Xms200m -Xmx200m -XX:PrintGC 等top命令观察到问题内存不断增长CPU占用率居高不下的进程。 top 查看所有的进程的cpu、内存占比。top -Hp pid 查看指定pid下各个线程的cpu、内存占比。关注谁比较高有可能是gc的线程jps定位具体java进程进一步可以使用jstack pid 对进程中的各个线程查看。尤其对WAITING的多个线程要关注。jinfo pidjstat -gc 动态观察gc情况 / 阅读GC日志发现频繁GC / arthas观察 / jconsole    jstat -gc pid 500 : 每隔500毫秒打印GC的情况jmap -histo pid | head -20 查找有多少对象产生jmap -dump:formatb,filexxx pid / jmap -histo修改参数 -Xms20m -Xmx20m -XX:UseParallelGC -XX:HeapDumpOnOutOfMemoryError使用MAT / jhat 进行dump文件分析 4、性能评价/测试指标 4.1、停顿时间或响应时间 提交请求和返回该请求的响应之间使用的时间一般比较关注平均响应时间。常用操作的响应时间列表 在垃圾回收环节中 暂停时间执行垃圾收集时程序的工作线程被暂停的时间。-XX:MaxGCPauseMillis 4.2、吞吐量 对单位时间内完成的工作量(请求)的量度在GC中运行用户代码的时间占总运行时间的比例总运行时间程序的运行时间内存回收的时间   吞吐量为 1 -1/(1n)。-XX:GCTimeRation 4.3、并发数 同一时刻对服务器有实际交互的请求数。例如1000个人同时在线估计并发数在5% -15%之间也就是同时并发量50 - 150之间。 4.4、内存占用 Java 堆区所占的内存大小 4.5、相互间的关系 以高速公路通行状况为例吞吐量就是每天通过高速公路收费站的车辆的数据也可以理解为收费站收取的高速费并发数就是高速公路上正在行驶的车辆的数目对应的响应时间就是车速。 5、性能调优案例 5.1、案例一提高服务吞吐量 初始jmeter配置下载并导入标题栏的 jmx 资源 首先在Linux服务器中安装一个Tomcat服务并在 ./bin 目录下配置以下脚本信息任意新建一个 sh 脚本填写以下信息即可。配置完成后直接启动 Tomcat 服务参数将自动读取并生效 #堆空间的初始大小 export CATALINA_OPTS$CATALINA_OPTS -Xms30m #伊甸园的比例分配 export CATALINA_OPTS$CATALINA_OPTS -XX:SurvivorRatio8 #堆空间的最大大小 export CATALINA_OPTS$CATALINA_OPTS -Xmx30m #使用并行收集器 export CATALINA_OPTS$CATALINA_OPTS -XX:UseParallelGC #打印GC的详细日志信息 export CATALINA_OPTS$CATALINA_OPTS -XX:PrintGCDetails #指定元空间大小 export CATALINA_OPTS$CATALINA_OPTS -XX:MetaspaceSize64m #打印GC日志时输出当前的时间戳信息 export CATALINA_OPTS$CATALINA_OPTS -XX:PrintGCDateStamps #生成GC日志的文件路径 export CATALINA_OPTS$CATALINA_OPTS -Xloggc:/opt/apache-tomcat-8.5.99/logs/gc.log 30m 堆空间5w 请求下的吞吐量 120m堆空间5w 请求下的吞吐量 这边对比看吞吐量确实有轻微提升但并不明显。如果多次压测甚至 30m 堆空间的平均吞吐量会更高一点不过 gc 状况不是很好。而120m 堆空间的 gc 状况会很好看几乎没有 FullGC对前端的延迟与体验会好很多。按理正常吞吐量应该也会更高才对不知道为什么我这边验证的并没有。可能与使用环境和软件的版本有关我使用的环境是 apache-tomcat-8.5.99Linux服务 jdk1.8.0_352jmeter运行于 win10 64位jdk1.8.0_321Tomcat 服务运行于 Centos7 虚机 5.2、案例二JIT优化 5.2.1、堆是分配对象的唯一选择吗 引自《深入理解Java虚拟机中》随着JIT编译期的发展与逃逸分析技术逐渐成熟栈上分配、标量替换优化技术将会导致一些微妙的变化所有的对象都分配到堆上也渐渐变得不那么“绝对”了。 在Java虚拟机中对象是在Java堆中分配内存的这是一个普遍的常识。但是有一种特殊情况那就是如果经过逃逸分析(Escape Analysis)后发现一个对象并没有逃逸出方法的话那么就可能被优化成栈上分配。这样就无需在堆上分配内存也无须进行垃圾回收了。这也是最常见的堆外存储技术。 此外前面提到的基于OpenJDK深度定制的TaoBaoVM其中创新的GCIHGC invisible heap技术实现off-heap将生命周期较长的Java对象从heap中移至heap外并且GC不能管理GCIH内部的Java对象以此达到降低GC的回收频率和提升GC的回收效率的目的。 所以可以说是也可以说不是因为对象的全部信息在栈上也没法完全保存毕竟空间也没那么大。 5.2.2、什么是逃逸 刚刚又提到一个名词叫“逃逸分析”那什么是逃逸呢。在Java中简单来说就是在一个方法里也就是一个栈帧里创建的任意对象、变量。只要是跟随这个方法的生存和死亡的就叫没有逃逸。就是说这个方法的用到的变量、对象没有提供给外部的任何方法使用。 而反之就是发生了逃逸从创建的这个方法里逃到其它方法里进行使用。対这类对象的分析处理就是逃逸分析了。 5.2.3、了解编译的开销成本 1、时间开销 解释器执行抽象化 输入的代码 - [ 解释器 解释执行 ] - 执行结果 JIT编译执行抽象化 输入的代码 - [ 编译器 编译 ] - 编译后的代码 - [ 执行 ] - 执行结果 说JIT比解释快其实说的是“执行编译后的代码”比“解释器解释执行”要快并不是说“编译”这个动作比“解释”这个动作快。JIT编译再怎么快至少也比解释执行一次略慢一些而要得到最后的执行结果还得再经过一个“执行编译后的代码”的过程。所以对“只执行一次”的代码而言解释执行其实总是比JIT编译执行要快。怎么算是只执行一次的代码呢粗略说下面条件同时满足时就是严格的只执行一次。只有对频繁执行的代码热点代码JIT编译才能保证有正面的收益。 只被调用一次例如类的构造器class initializer()没有循环对只执行一次的代码做JIT编译再执行可以说是得不偿失。对只执行少量次数的代码JIT编译带来的执行速度的提升也未必能抵消掉最初编译带来的开销。 2、空间开销 对一般的Java方法而言编译后代码的大小相对于字节码的大小膨胀比达到10是很正常的。同上面说的时间开销一样这里的空间开销也是只有对执行频繁的代码才值得编译如果把所有代码都编译则会显著增加代码所占空间导致代码爆炸。这也就解释了为什么有些JVM会选择不总是做JIT编译而是选择用解释器JIT编译器的混合执行引擎。 5.2.4、什么是逃逸分析 如何将堆上的对象分配到栈这里使用的手段就是逃逸分析。逃逸分析(Escape Analysis)是目前Java虚拟机中比较前沿的优化技术。这是一种可以有效减少Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。 通过逃逸分析Java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。 逃逸分析的基本行为就是分析对象动态作用域 当一个对象在方法中被定义后对象只在方法内部使用则认为没有发生逃逸。当一个对象在方法中被定义后它被外部方法所引用则认为发生逃逸。例如作为调用参数传递到其他地方中。 //未发生逃逸 public void my_method() {V v new V();//use v//......v null; } 没有发生逃逸的对象则可以分配到栈上随着方法执行的结束栈空间就被移除。逃逸分析包括 全局变量赋值逃逸方法返回值逃逸实例引用发生逃逸线程逃逸:赋值给类变量或可以在其他线程中访问的实例变量 5.2.5、关于逃逸分析配置参数 在JDK 6u23版本之后HotSpot中默认就已经开启了逃逸分析。如果使用的是较早的版本开发人员则可以通过 通过选项“-XX:DoEscapeAnalysis”显式开启逃逸分析通过选项“-XXPrintEscapeAnalysis”查看逃逸分析的筛选结果。参数-XX:EliminateAllocations开启了标量替换(默认打开)允许将对象打散分配在栈上。 注意这里需要留意下因为1和3可以单独禁用所以这两个配置参数必须组合并启用才会生效在jdk 6u23之后都是默认启用的。 5.2.6、逃逸分析代码举例 示例一 //此时发生了逃逸 public static StringBuffer createStringBuffer(String s1, String s2) {StringBuffer sb new StringBuffer();sb.append(s1);sb.append(s2);return sb; }//上述代码如果想要StringBuffer sb不逃出方法可以这样写 public static String createStringBuffer(String s1, String s2) {StringBuffer sb new StringBuffer();sb.append(s1);sb.append(s2);return sb.toString(); } 示例二 package blnp.net.cn.jvm.demos.escape;/*** p逃逸分析/p* 如何快速的判断是否发生了逃逸分析大家就看new的对象实体是否有可能在方法外被调用。** author lyb 2045165565qq.com* createDate 2024/4/16 14:27*/ public class EscapeAnalysis {public EscapeAnalysis obj;/*** 用途:方法返回EscapeAnalysis对象发生逃逸* author liaoyibin* date 14:28 2024/4/16* params []* param**/public EscapeAnalysis getInstance(){return obj null? new EscapeAnalysis() : obj;}/*** 用途:为成员属性赋值发生逃逸* author liaoyibin* date 14:28 2024/4/16* params []* param**/public void setObj(){/*** 思考如果当前的obj引用声明为static的会发生逃逸吗** 答案肯定是会的只是此时的 obj 从成员变量升级成类的变量了。但依旧是被外部引用了* **/this.obj new EscapeAnalysis();}/*** 用途:对象的作用域仅在当前方法中有效没有发生逃逸* author liaoyibin* date 14:32 2024/4/16* params []* param**/public void useEscapeAnalysis(){EscapeAnalysis e new EscapeAnalysis();}/*** 用途:引用成员变量的值发生逃逸* author liaoyibin* date 14:33 2024/4/16* params []* param**/public void useEscapeAnalysis1(){//getInstance().xxx()同样会发生逃逸EscapeAnalysis e getInstance();}/*** 用途:外部传入也发生了逃逸* author liaoyibin* date 14:34 2024/4/16* params [e] * param e **/public void operate(EscapeAnalysis e){// e} } 5.2.7、栈上分配验证 使用逃逸分析编译器可以对代码做如下优化。栈上分配。将堆分配转化为栈分配。如果经过逃逸分析后发现一个对象并没有逃逸出方法的话那么就可能被优化成栈上分配。这样就无需在堆上分配内存也无须进行垃圾回收了。可以减少垃圾回收时间和次数。 JIT编译器在编译期间根据逃逸分析的结果发现如果一个对象并没有逃逸出方法的话就可能被优化成栈上分配。分配完成后继续在调用栈内执行最后线程结束栈空间被回收局部变量对象也被回收。这样就无须进行垃圾回收了。 package blnp.net.cn.jvm.demos.escape;/*** p栈上分配测试/p* emsp;emsp;启动参数-Xmx1G -Xms1G -XX:-DoEscapeAnalysis -XX:PrintGCDetails* 说明因为栈上分配配置在jdk6u32之后就默认开启为了测试对比区别这里先禁用查看下效果** 只要开启了逃逸分析就会判断方法中的变量是否发生了逃逸。h3如果没有发生了逃逸则会使用栈上分配/h3* author lyb 2045165565qq.com* createDate 2024/4/16 14:37*/ public class StackAllocation {public static void main(String[] args) {long start System.currentTimeMillis();//创建对象个数Integer objNums 10000000;for (int i 0; i objNums; i) {alloc();}// 查看执行时间long end System.currentTimeMillis();System.out.println(花费的时间为 (end - start) ms);try {//为了方便查看堆内存中对象个数线程sleepThread.sleep(1000000);} catch (InterruptedException e1) {e1.printStackTrace();}}private static void alloc() {//是否发生逃逸 没有User user new User();}static class User {} }未启用栈上分配配置时执行示例代码创建 1000w 个对象的耗时 此时查看当前进程堆创建实例信息如下所示 #查看指定进程创建实例数 jmap histo {pid} 特别提示这里可能有人会遇到跟我一样的问题就是使用 jvisualvm 时打开抽样器发现 CPU、内存这块无法进行抽样操作。 这是因为权限不够的问题将 jvisualvm 用管理员权限打开即可。 此时我们开启下栈上分配配置。对比下执行效果也就是将参数 “-XX:-DoEscapeAnalysis” 改为 “-XX:DoEscapeAnalysis” 或者去掉该启动参数因为默认就是启用的。 对比后发现这个有开启和没有开启简直是一个天一个地。所以非常建议开发中能使用局部变量的就不要使用在方法外定义。 备注这里细心的人可能会发现我刚开始是在Windows机器里操作实验的后面又换到Linux机器上操作了。         这是因为我在Windows机器上测试没有效果不知道为什么启用和没启用都是一样的。目前是没有找到具体的原因有懂的朋友方便指导一二。我用的环境信息大致如下 IDEA 2020.3Windows 10 企业版 LTSC  64位 版本号21H2Oracle JDK 1.8.0_321-b07 5.2.8、同步消除测试 同步省略也叫同步消除。如果一个对象被发现只能从一个线程被访问到那么对于这个对象的操作可以不考虑同步。 线程同步的代价是相当高的同步的后果是降低并发性和性能。在动态编译同步块的时候JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果没有那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这样就能大大提高并发性和性能。这个取消同步的过程就叫同步省略也叫锁消除。 package blnp.net.cn.jvm.demos.escape;/*** p同步消除测试/p** author lyb 2045165565qq.com* createDate 2024/4/16 16:12*/ public class SynchronizedTest {/*** 用途:代码中对hollis这个对象进行加锁但是hollis对象的生命周期只在 fun() 方法中* 并不会被其他线程所访问到所以在JIT编译阶段就会被优化掉。* author liaoyibin* date 16:13 2024/4/16* params []* param**/public void fun() {/*** 问题字节码文件中会去掉hollis吗* 答案不会编译只是生成字节码文件。* **//** 优化前 **/Object hollis new Object();synchronized(hollis) {System.out.println(hollis);}/** 优化后 **//*** Object hollis new Object();* System.out.println(hollis);* **/} }5.2.9、标量替换测试 标量Scalar是指一个无法再分解成更小的数据的数据。Java中的原始数据类型就是标量。相对的那些还可以分解的数据叫做聚合量AggregateJava中的对象就是聚合量因为他可以分解成其他聚合量和标量。 在JIT阶段如果经过逃逸分析发现一个对象不会被外界访问的话那么经过JIT优化就会把这个对象拆解成若干个其中包含的若干个成员变量来代替。这个过程就是标量替换。 标量替换参数设置 参数-XX:EliminateAllocations开启了标量替换(默认打开)允许将对象打散分配在栈上。 代码示例一 public static void main(String[] args) {alloc(); }private static void alloc() {Point point new Point1,2;System.out.println(point.xpoint.x; point.ypoint.y); }class Point{private int x;private int y; }//经过标量替换后就会变成 private static void alloc() {int x 1;int y 2;System.out.println(point.xx; point.yy); } 可以看到Point这个聚合量经过逃逸分析后发现他并没有逃逸就被替换成两个标量了。那么标量替换有什么好处呢就是可以大大减少堆内存的占用。因为一旦不需要创建对象了那么就不再需要分配堆内存了。 代码示例二 package blnp.net.cn.jvm.demos.escape;/*** p标量替换测试/p* 启动参数 -server -Xmx100m -Xms100m -XX:DoEscapeAnalysis -XX:PrintGCDetails -XX:-EliminateAllocations* 结论Java中的逃逸分析其实优化的点就在于对栈上分配的对象进行标量替换。* author lyb 2045165565qq.com* createDate 2024/4/16 16:34*/ public class ScalarReplace {public static class User {public int id;public String name;}public static void alloc() {//未发生逃逸User u new User();u.id 5;u.name http://blnp.net.cn;}public static void main(String[] args) {long start System.currentTimeMillis();int nums 10000000;for (int i 0; i nums; i) {alloc();}long end System.currentTimeMillis();System.out.println(花费的时间为 (end - start) ms);} } 禁用标量替换的效果 启用标量替换的效果 上述代码在主函数中进行了 1000w 次alloc。调用进行对象创建由于User对象实例需要占据约16字节的空间因此累计分配空间达到将近 152m。如果堆空间小于这个值就必然会发生 GC。因此在禁用标量替换时日志里有明显的GC操作。 参数-server启动Server模式因为在Server模式下才可以启用逃逸分析。参数 -XX:DoEscapeAnalysis启用逃逸分析参数-Xmx100m指定了堆空间最大为10MB参数-XX:PrintGC将打印GC 日志。参数-XX:EliminateAllocations开启了标量替换(默认打开)允许将对象打散分配在栈上 比如对象拥有id和name两个字段那么这两个字段将会被视为两个独立的局部变量进行分配。 5.3、案例三合理配置堆内存 5.3.1、如何合理配置 之前提到过增加内存可以提高系统的性能而且效果显著那么随之带来的一个问题就是我们增加多少内存比较合适如果内存过大那么如果产生FullGC的时候GC时间会相对比较长如果内存较小那么就会频繁的触发GC在这种情况下我们该如何合理的适配堆内存大小呢 依据的原则是根据Java Performance里面的推荐公式来进行设置。 Java整个堆大小设置Xmx 和 Xms设置为老年代存活对象的3-4倍即FullGC之后的老年代内存占用的3-4倍。方法区永久代 PermSize和MaxPermSize 或 元空间 MetaspaceSize 和 MaxMetaspaceSize设置为老年代存活对象的1.2-1.5倍。年轻代Xmn的设置为老年代存活对象的1-1.5倍。老年代的内存大小设置为老年代存活对象的2-3倍。 但是这个也不是绝对的也就是说这给的是一个参考值根据多种调优之后得出的一个结论大家可以根据这个值来设置一下我们的初始化内存在保证程序正常运行的情况下我们还要去查看GC的回收率GC停顿耗时内存里的实际数据来判断Full GC是基本上不能有的如果有就要做内存Dump分析然后再去做一个合理的内存分配。 5.3.2、如何计算老年代存活对象 1、方式一查看日志 【推荐方式】JVM参数中添加GC日志GC日志中会记录每次FullGC之后各代的内存大小观察老年代GC之后的空间大小。可观察一段时间内比如2天的FullGC之后的内存情况根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小可根据多次FullGC之后的内存大小取平均值。 2、方式二强制触发 FullGC 该方式会影响线上服务慎用会有STW方式1的方式比较可行但需要更改JVM参数并分析日志。同时在使用CMS回收器的时候有可能不能触发FullGC所以日志中并没有记录FullGC的日志。在分析的时候就比较难处理。 所以有时候需要强制触发一次FullGC来观察FullGC之后的老年代存活对象大小。 建议的操作方式为在强制FullGC前先把服务节点摘除FullGC之后再将服务挂回可用节点对外提供服务在不同时间段触发FullGC根据多次FullGC之后的老年代内存情况来预估FullGC之后的老年代存活对象大小。 如何强制触发Full GC? jmap -dump:live,formatb,fileheap.bin pid 将当前的存活对象dump到文件此时会触发FullGCjmap -histo:live pid 打印每个class的实例数目,内存占用,类全名信息.live子参数加上后,只统计活的对象数量. 此时会触发FullGC在性能测试环境可以通过Java监控工具来触发FullGC比如使用VisualVM和JConsoleVisualVM集成了JConsoleVisualVM或者JConsole上面有一个触发GC的按钮。 5.3.3、案例分析 启动参数配置 -XX:PrintGCDetails -XX:MetaspaceSize64m -Xss512K -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPathheap/heapdump3.hprof  -XX:SurvivorRatio8  -XX:PrintGCDateStamps  -Xms1024M  -Xmx1024M -Xloggc:log/gc-oom3.log 项目启动通过jmeter访问10000次主要是看项目是否可以正常运行之后查看gc状态 jstat -gc {pid} YGC平均耗时 0.12s * 1000/7 17.14msFGC未产生 看起来似乎不错YGC触发的频率不高FGC也没有产生但这样的内存设置是否还可以继续优化呢是不是有一些空间是浪费的呢。 为了快速看数据我们使用了方式2通过命令 jmap -histo:live pid 产生几次FullGCFullGC之后使用的jmap -heap 来看的当前的堆内存情况。观察老年代存活对象大小 #或者直接查看GC日志查看一次FullGC之后剩余的空间大小 jmap -heap {pid} 可以看到存活对象占用内存空间大概13.36M老年代的内存占用为683M左右。 按照整个堆大小是老年代FullGC之后的3-4倍计算的话设置堆内存情况如下 Xmx14 * 3 42M 至 14 * 4 56M 之间 我们修改堆内存状态如下 -XX:PrintGCDetails -XX:MetaspaceSize64m -Xss512K -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPathheap/heapdump.hprof -XX:SurvivorRatio8 -XX:PrintGCDateStamps -Xms60M -Xmx60M -Xloggc:log/gc-oom.log 修改完之后我们查看一下GC状态 请求之后YGC平均耗时 0.195s * 1000/68 2.87msFGC未产生。         整体的GC耗时减少。但GC频率比之前的 1024M 时要多了一些。依然未产生 FullGC 所以我们内存设置为 60M 也是比较合理的相对之前节省了很大一块内存空间,所以本次内存调整是比较合理的。依然是手动触发Full 查看堆内存结构 在内存相对紧张的情况下可以按照上述的方式来进行内存的调优 找到一个在GC频率和GC耗时上都可接受的一个内存设置可以用较小的内存满足当前的服务需要。但当内存相对宽裕的时候可以相对给服务多增加一点内存可以减少GC的频率GC的耗时相应会增加一些。 一般要求低延时的可以考虑多设置一点内存 对延时要求不高的可以按照上述方式设置较小内存。  如果在垃圾回收日志中观察到OutOfMemoryError,尝试把Java堆的大小扩大到物理内存的80%~90%。尤其需要注意的是堆空间导致的OutOfMemoryError以及一定要增加空间。 比如说增加 -Xms 和 -Xmx 的值来解决 old 代的 OutOfMemoryError增加-XX:PermSize和-XX:MaxPermSize来解决permanent代引起的OutOfMemoryErrorjdk7之前增加-XX:MetaspaceSize和-XX:MaxMetaspaceSize来解决Metaspace引起的OutOfMemoryErrorjdk8之后 记住一点Java堆能够使用的容量受限于硬件以及是否使用64位的JVM。在扩大了Java堆的大小之后再检查垃圾回收日志直到没有OutOfMemoryError为止。如果应用运行在稳定状态下没有OutOfMemoryError就可以进入下一步了计算活动对象的大小。 5.3.4、估算GC的频率 正常情况我们应该根据我们的系统来进行一个内存的估算这个我们可以在测试环境进行测试最开始可以将内存设置的大一些比如4G这样当然这也可以根据业务系统估算来的。 比如从数据库获取一条数据占用128个字节需要获取1000条数据那么一次读取到内存的大小就是128 B/1024 Kb/1024M* 1000 0.122M 那么我们程序可能需要并发读取比如每秒读取100次那么内存占用就是0.122*100 12.2M  如果堆内存设置1个G那么年轻代大小大约就是333M那么333M*80% / 12.2M  21.84s 也就是说我们的程序几乎每分钟进行两到三次youngGC。这样可以让我们对系统有一个大致的估算。 0.122M * 100 12.2M /秒  ---Eden区1024M * 1/3新生代老年代1:2 * 80% 273M 273 / 12.2M 22.38s --- YGC  每分钟2-3次YGC 5.4、新生代与老年代比例大流量系统注意 JVM 参数设置为 # 打印日志详情          打印日志打印日期     初始化内存300M  最大内存300M   日志路径 -XX:PrintGCDetails   -XX:PrintGCDateStamps  -Xms300M  -Xmx300M -Xloggc:log/gc.log package blnp.net.cn.jvm.demos;/*** p使用ParallelGC的情况下不管是否开启了UseAdaptiveSizePolicy参数默认Eden与Survivor的比例都为6:1:1/p** author lyb 2045165565qq.com* createDate 2024/4/17 16:34*/ public class AdaptiveSizePolicyTest {public static void main(String[] args) {try {Thread.sleep(1000000);} catch (InterruptedException e) {e.printStackTrace();}} } 程序启动后我们可以先用命令查看一下堆内存分配是怎么样的 # 查看进程ID jps -l# 查看对应的进程ID的堆内存分配 jmap -heap 50044 新生代 ( Young ) 与老年代 ( Old ) 的比例为 1:2所以内存分配应该是新生代100M老年代 200M。结果大家可以看到我们的SurvivorRatio 8 但是新生代内存分配却不是 8:1:1 而是 6:1:1这是为什么呢 这是因为JDK 1.8 默认使用 UseParallelGC 垃圾回收器该垃圾回收器默认启动了 AdaptiveSizePolicy会根据GC的情况自动计算计算 Eden、From 和 To 区的大小所以这是由于JDK1.8的自适应大小策略导致的除此之外我们下面观察GC日志发现有很多类似这样的FULLGCErgonomics也是一样的原因。我们可以在jvm参数中配置开启和关闭该配置 # 开启-XX:UseAdaptiveSizePolicy# 关闭-XX:-UseAdaptiveSizePolicy 启动参数调整为 -XX:PrintGCDetails -XX:PrintGCDateStamps -Xms300M -Xmx300M -Xloggc:system/log/gc.log -XX:-UseAdaptiveSizePolicy 根据结果会发现此时即使关闭了调优策略依旧还是 6:1:1。这是因为如果JDK使用的是 ParallelGC 垃圾回收器的话单纯禁用策略是不会生效的还必须得明文带上启动参数-XX:SurvivorRatio8。关于查看JDK使用的是什么垃圾回收器可以通过该命令 java -XX:PrintCommandLineFlags -version 明文指定参数后 查看指定进程的启动参数配置信息jcmd {pid} VM.flags 注意事项 1、在 JDK 1.8 中如果使用 CMS无论 UseAdaptiveSizePolicy 如何设置都会将 UseAdaptiveSizePolicy 设置为 false不过不同版本的JDK存在差异 2、UseAdaptiveSizePolicy不要和SurvivorRatio参数显示设置搭配使用一起使用会导致参数失效 3、由于UseAdaptiveSizePolicy会动态调整 Eden、Survivor 的大小有些情况存在Survivor 被自动调为很小比如十几MB甚至几MB的可能这个时候YGC回收掉 Eden区后还存活的对象进入Survivor 装不下就会直接晋升到老年代导致老年代占用空间逐渐增加从而触发FULL GC如果一次FULL GC的耗时很长比如到达几百毫秒那么在要求高响应的系统就是不可取的。 因此对于面向外部的大流量、低延迟系统不建议启用此参数建议关闭该参数。如果不想动态调整内存大小以下是解决方案 保持使用 UseParallelGC显式设置 -XX:SurvivorRatio8。使用 CMS 垃圾回收器。CMS 默认关闭 AdaptiveSizePolicy。配置参数 -XX:UseConcMarkSweepGC 此外关于堆内存的自适应调节有如下三个参数调整堆是按照每次20%增长按照每次5%收缩 young区增长量默认20%-XX:YoungGenerationSizeIncrementYold区增长量默认20%-XX:TenuredGenerationSizeIncrementT收缩量默认5%-XX:AdaptiveSizeDecrementScaleFactorD 5.5、案例四CPU占用排查 package blnp.net.cn.jvm.demos;/*** p/p** author lyb 2045165565qq.com* createDate 2024/4/17 17:18*/ public class JstackDeadLockDemo {/*** 必须有两个可以被加锁的对象才能产生死锁只有一个不会产生死锁问题*/private final Object obj1 new Object();private final Object obj2 new Object();public static void main(String[] args) {new JstackDeadLockDemo().testDeadlock();}private void testDeadlock() {Thread t1 new Thread(() - calLock_Obj1_First());Thread t2 new Thread(() - calLock_Obj2_First());t1.start();t2.start();}/*** 先synchronized obj1再synchronized obj2*/private void calLock_Obj1_First() {synchronized (obj1) {sleep();System.out.println(已经拿到obj1的对象锁接下来等待obj2的对象锁);synchronized (obj2) {sleep();}}}/*** 先synchronized obj2再synchronized obj1*/private void calLock_Obj2_First() {synchronized (obj2) {sleep();System.out.println(已经拿到obj2的对象锁接下来等待obj1的对象锁);synchronized (obj1) {sleep();}}}/*** 为了便于让两个线程分别锁住其中一个对象* 一个线程锁住obj1然后一直等待obj2* 另一个线程锁住obj2然后一直等待obj1* 然后就是一直等待死锁产生*/private void sleep() {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}} }如果线程死锁那么线程一直在占用CPU这样就会导致CPU一直处于一个比较高的占用率。所示我们解决问题的思路应该是 首先查看java进程ID根据进程 ID 检查当前使用异常线程的pid例如把线程pid变为16进制如 31695 - 7bcf  然后得到0x7bcfjstack 进程的pid | grep -A20  0x7bcf  得到相关进程的代码 (鉴于我们当前代码量比较小线程也比较少所以我们就把所有的信息全部导出来) 实操步骤 第一步获取运行Java进程的ID 第二步观察服务器CPU资源占用情况 第三步观察Java进程子线程的使用情况 top -p {Java进程ID} -H 此时会发现子线程列表里1872/1873比较可疑。我们这边将子线程ID进一步换算排查。 第四步换算ID #将最耗cpu的线程id转为16进制输出 printf %x \n 1672 注此处id是子线程pid printf %x \n 1872 第五步分析原因 #使用jstack查看分析原因 # jstack {Java进程ID} | grep {子线程换算ID} -A {查看多少行} jstack 1862 | grep 750 -A 25 第六步问题解决 调整锁的顺序保持一致或者采用定时锁一段时间后如果还不能获取到锁就释放自身持有的所有锁 6、面题场景 6.1、场景一 问题一有一个50万PV的资料类网站从磁盘提取文档到内存原服务器是32位的1.5G的堆用户反馈网站比较缓慢。因此公司决定升级新的服务器为64位16G的堆内存结果用户反馈卡顿十分严重反而比以前效率更低了 6.1.1、为什么原网站慢 频繁的GC,STW时间比较长响应时间慢 6.1.2、为什么会更卡顿 内存空间越大FGC时间更长延迟时间更长 6.1.3、如何解决 垃圾回收器parallel GC ;  ParNew CMS ; G1配置GC参数-XX:MaxGCPauseMillis 、 -XX:ConcGCThreads 根据log日志、dump文件分析优化内存空间的比例 6.2、场景二 6.2.1、系统CPU经常100%如何调优 详见《5.5、案例四CPU占用排查》注意 工作中有时候是工作线程100%占用了CPU还有可能是垃圾回收线程占用了100%。 7、代码优化注意事项 7.1、尽可能使用局部变量 调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快其他变量如静态变量、实例变量等都在堆中创建速度较慢。另外栈中创建的变量随着方法的运行结束这些内容就没了不需要额外的垃圾回收。 7.2、尽量减少对变量的重复计算 明确一个概念对方法的调用即使方法中只有一句语句也是有消耗的。所以例如下面的操作 for (int i 0; i list.size(); i) {//todo } 建议替换为 int length list.size(); for (int i 0, i length; i){//todo } 这样在list.size()很大的时候就减少了很多的消耗。 7.3、尽量采用懒加载的策略即在需要的时候才创建 String str aaa; if (i 1){list.add(str); }//建议替换成 if (i 1){String str aaa;list.add(str); } 7.4、异常不应该用来控制程序流程 异常对性能不利。抛出异常首先要创建一个新的对象Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方 法fillInStackTrace()方法检查堆栈收集调用跟踪信息。只要有异常被抛出Java虚拟机就必须调整调用堆栈因为在处理过程中创建了一个新的对象。异常只能用于错误处理不应该用来控制程序流程。 7.5、不要将数组声明为public static final 因为这毫无意义这样只是定义了引用为 static final数组的内容还是可以随意改变的将数组声明为 public 更是一个安全漏洞这意味着这个数组可以被外部类所改变。 7.6、不要创建一些不使用的对象不要导入一些不使用的类 这毫无意义如果代码中出现 The value of the local variable i is not used、The import java.util is never used那么请删除这些无用的内容。 7.7、程序运行过程中避免使用反射 反射是Java提供给用户一个很强大的功能功能强大往往意味着效率不高。不建议在程序运行过程中使用尤其是频繁使用反射机制特别是 Method的invoke方法。 如果确实有必要一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存。 7.8、使用数据库连接池和线程池 这两个池都是用于重用对象的前者可以避免频繁地打开和关闭连接后者可以避免频繁地创建和销毁线程。 7.9、容器初始化时尽可能指定长度 容器初始化时尽可能指定长度如new ArrayList(10); new HashMap(32);  避免容器长度不足时扩容带来的性能损耗。 7.10、ArrayList随机遍历快LinkedList添加删除快 7.11、使用 Entry 遍历 Map MapString,String map new HashMap(); for (Map.EntryString,String entry : map.entrySet()) {String key entry.getKey();String value entry.getValue(); }//避免使用这种方式 MapString,String map new HashMap(); for (String key : map.keySet()) {String value map.get(key); } 7.12、String尽量少用正则表达式 正则表达式虽然功能强大但是其效率较低除非是有需要否则尽可能少用。replace() 不支持正则replaceAll() 支持正则。如果仅仅是字符的替换建议使用replace()。 7.13、对资源的close()建议分开操作 try{XXX.close();YYY.close(); }catch (Exception e){... }// 建议改为 try{XXX.close(); }catch (Exception e){... }try{YYY.close(); }catch (Exception e){... }
http://www.zqtcl.cn/news/410829/

相关文章:

  • 单位建设网站装修公司需要什么资质
  • 做做做网站做网站赚外快
  • 网站备案后应该做什么网站流量监测
  • 开发网站用什么语言做名片的网站叫什么来着
  • 织梦做网站好不好iis中的网站启动不了
  • 临汾住房与城乡建设厅网站迎访问中国建设银行网站_
  • 织梦做的网站首页幻灯片怎么不能显示北大青鸟网站建设课程
  • 做淘客的网站有哪些延安市住建建设网站
  • 南京林业大学实验与建设网站现在都用什么软件搜索附近的人
  • 建站系统wordpress下载亚马逊雨林十大恐怖生物
  • 凡科网做网站怎么样专业团队电影
  • 有什么有趣的网站移动网站排名怎么做
  • 深圳网站建设专家wordpress 4.5下载地址
  • 网站建设公司公司我我提供一个平台wordpress如何去版权信息
  • seo怎么给网站做外链受欢迎的网站建设教程
  • 网站建设使用多语言河南电商网站设计
  • 网站搭建有免费的吗网站地图生成代码
  • 建设公司网站要注意什么投资小利润高的小生意
  • 网站建设需要做哪些工作做胃镜需那好天津津门网站A
  • 做网站申请域名的流程辽宁省工程造价网
  • 网站系统维护一般多长时间金华高端网站设计
  • 做网站公司销售开场白企业网站规划与开发
  • 兰州新区建站不锈钢网站建设
  • 淘宝小网站怎么做的电商网站有哪些
  • 哪些网站可以做画赚钱wordpress go跳转页
  • 怎么做新网站上线通稿深圳罗湖区网站建设公司
  • php 企业网站做网站可以赚钱吗
  • 局域网视频网站建设点播系统长沙3合1网站建设价格
  • 静态网站 服务器合肥做个网站什么价格
  • 宁阳网站设计家电网站设计方案