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

m开头的可以做网站的软件wordpress专用空间

m开头的可以做网站的软件,wordpress专用空间,北京建设银行对公网站,微信小程序开发注册JVM实战笔记-随笔 前言字节码如何查看字节码文件jclasslibJavapArthasArthurs监控面板Arthus查看字节码信息 内存调优内存溢出的常见场景解决内存溢出发现问题Top命令VisualVMArthas使用案例 Prometheus Grafana案例 堆内存情况对比内存泄漏的原因:代码中的内存泄漏并发请求问… JVM实战笔记-随笔 前言字节码如何查看字节码文件jclasslibJavapArthasArthurs监控面板Arthus查看字节码信息 内存调优内存溢出的常见场景解决内存溢出发现问题Top命令VisualVMArthas使用案例 Prometheus Grafana案例 堆内存情况对比内存泄漏的原因:代码中的内存泄漏并发请求问题 Jmeter模拟并发请求插件增强(不是必须可能自带)案例使用 诊断问题MAT工具使用和内存快照Jmap命令案例: 没有溢出但是持续增长怎么分析——jmap命令 分析超大堆的内存快照 修复问题案例1:分页查询文章接口导致的内存溢出实践如何查看是什么占用内存的是什么数据?如果是线程的问题怎么查看是哪个接口呢?如何根据接口确定是否相关对象占用过大?如何模拟重现问题解决案例1问题 案例2: Mybatis导致的内存溢出实践分析SQL相关的内存占用解决案例2 案例3:导出大文件内存溢出搭建k8s集群 案例4:ThreadLocal使用时占用大量内存案例5文章内容审核接口的内存溢出设计1设计2设计3: 两种方式的优缺点在线定位jmap在线定位步骤btrace在线定位步骤 小结 GC调优GC调优的核心分成三部分GC调优的指标吞吐量:垃圾回收吞吐量 延迟内存的使用量GCeasy工具 GC调优的方法工具jstat工具VisualVMVisualGC的GC插件 Prometheus GrafanaGC 日志GC ViewerGC easy以上是所有gc调优相关的工具 常见的GC模式一、正常情况二、缓存对象过多三、内存泄漏四、持续的FullGC五、元空间不足导致的FULLGC 解决GC问题的手段如何通过oracle官网找到jdk的相关参数(重要)优化基础JVM参数-Xmx 和 –Xms-XX:MaxMetaspaceSize 和 –XX:MetaspaceSize-Xss不建议手动设置的参数-Xmn‐XX:SurvivorRatio‐XX:MaxTenuringThreshold 其他参数 -XX:DisableExplicitGC-XX:HeapDumpOnOutOfMemoryError 垃圾回收器的选择优化垃圾回收器的参数案例 实战: 内存调优GC调优GC调优的核心流程 性能调优线程转储方式Jstack命令VisualVM 案例案例1CPU占用率高问题的解决方案遗留问题案例补充 案例2: 接口响应时间很长的问题Arthas的trace命令Arthas的watch命令 案例3: 定位偏底层的性能问题案例4线程被耗尽问题 更精细的性能测试JIT对程序性能的影响正确地测试代码性能JMH环境搭建 案例:日期格式化方法性能测试 性能调优综合实战总结 前言 该文章包含jvm调优工具的学习记录以及常用的jvm调优工具仅随笔不完善地方请指出 字节码 如何查看字节码文件 本地文件可以使用jclasslib工具查看开发环境使用jclasslib插件 服务器上文件使用javap命令直接查看也可以通过arthas的dump命令导出字节码文件再查看本地文件。还可以使用jad命令反编译出源代码 javapdump jclasslib ——用于本地查看字节码文件 jclasslib工具查看字节码文件Github地址 https://github.com/ingokegel/jclasslibjclasslib也有Idea插件版本建议开发时使用Idea插件版本可以在代码编译之后实时看到字节码文件内容 安装插件jclasslib左上角View的show…class Javap ——用于查看服务器字上的节码文件java自带的反编译工具可以通过控制台查看字节码文件的内容 适合在服务器上面查看字节码文件 直接在shell输入javap查参数输入 javap -v 字节码文件名称 查看具体的字节码信息如果是jar包需要jar -xvf 命令解压 进入BOOT-INF的com 目录下找到字节码文件进行展示 案例 Arthas ——用于查看服务器运行中的字节码文件Arthas 是一款线上监控诊断产品通过全局视角实时查看应用 load、内存、gc、线程的状态信息并能在不修改应用代码的情况下对业务问题进行诊断大大提升线上问题排查效率。官网https://arthas.aliyun.com/doc/ 查看字节码信息内存监控垃圾回收监控应用热点监控 Arthurs的jar包链接https://pan.baidu.com/s/1gzl7MuXeHk1CkH2z4EzkWg?pwdk76r 提取码k76r 使用Arthurs的方式 直接像运行java的jar包一样在服务器运行 Arthurs监控面板 案例 直接运行Arthur-boot.jar展示运行的java程序 比如想要进入5的程序直接键入5执行dashboard -i 2000 -n 3 暂停2s执行三次展示 Arthus查看字节码信息 通过dump命令将类的字节码信息存储到某个文件内(前提是启动Arthurs进入这个进程) 如下 通过jad命令通过字节码查看原代码 内存调优 什么是内存泄漏监控java内存的工具内存泄漏的常见场景内存泄漏的解决方法 内存溢出的常见场景 大型java后端应用在处理用户请求之后没有及时删除用户信息。随着用户请求数据量越来越多内存泄漏的对象沾满了堆内存导致内存溢出。直接导致用户请求无法处理重启可以恢复但一段时间后依旧会溢出。 第二种常见场景是分布式任务调度系统如Elastic-job、Quartz等进行任务调度时被调度的Java应用在调度任务结束中出现了内存泄漏最终导致多次调度之后内存溢出。这种产生的内存溢出会导致应用执行下次的调度任务执行。同样重启可以恢复应用使用但是在调度执行一段时间之后依然会出现内存溢出 解决内存溢出 发现问题 Top命令 ——用于服务器查看进程CPU,内存占用等信息 top命令是linux下用来查看系统信息的一个命令它提供给我们去实时地去查看系统的资源比如执行时的进程、线程和系统参数等信息占用内存CPU情况等。进程使用的内存为RES常驻内存- SHR共享内存简单 VisualVM ——多用于监测分析测试环境的内存问题(生产环境会占用线程资源慎用) VisualVM是多功能合一的Java故障排除工具并且他是一款可视化工具整合了命令行 JDK 工具和轻量级分析功能功能非常强大这款软件在Oracle JDK 6~8 中发布但是在 Oracle JDK 9 之后不在JDK安装目录下需要单独下载。下载地址https://visualvm.github.io/ jdk8在bin目录下双击打开idea也有相关插件: visualVMvisualVM远程访问服务器(不要在生产环境使用会占用主线程生产环境禁止使用!!!) 需要按下图进行开发权限端口 远程连接开发端口配置 远程连接寻找jax连接 Arthas ——监控生产环境的内存占用情况Arthas 是一款线上监控诊断产品通过全局视角实时查看应用 load、内存、gc、线程的状态信息并能在不修改应用代码的情况下对业务问题进行诊断包括查看方法调用的出入参、异常监测方法执行耗时类加载信息等大大提升线上问题排查效率。 功能强大不止于监控基础的信息还能监控单个方法的执行耗时等细节内应用于集群管理部分高级功能使用门槛较高 使用案例 在Spring Boot程序中添加arthas的依赖(支持Spring Boot2)在配置文件中添加tunnel服务端的地址便于tunnel去监控所有的程序。 dependencygroupIdcom.taobao.arthas/groupIdartifactIdarthas-spring-boot-starter/artifactIdversion3.7.1/version/dependency//配置相关 arthas:#tunnel地址目前是部署在同一台服务器正式环境需要拆分tunnel-server: ws://localhost:7777/ws#tunnel显示的应用名称直接使用应用名app-name: ${spring.application.name}#arthas http访问的端口和远程连接的端口http-port: 8888telnet-port: 9999将tunnel服务端程序部署在某台服务器上并启动。 nohup java -jar -Darthas.enable-detail-pagestrue arthas-tunnel-server-3.7.1-fatjar.jar 也可以手动指定端口分布式部署 启动java程序打开tunnel的服务端页面查看所有的进程列表并选择进程进行arthas的操 输入: IP:端口/apps.html 打开控制台Prometheus Grafana ——专门的运维来搭建的监控系统用于监控微服务集群PrometheusGrafana是企业中运维常用的监控方案其中Prometheus用来采集系统或者应用的相关数据同时具备告警功能。Grafana可以将Prometheus采集到的数据以可视化的方式进行展示。 Java程序员要学会如何读懂Grafana展示的Java虚拟机相关的参数支持系统级别和应用级别的监控比如linux操作系统、Redis、MySQL、Java进程。支持告警并允许自定义告警指标通过邮件、短信等方式尽早通知相关人员进行处理环境搭建较为复杂一般由运维人员完成 案例 阿里云搭建可观测监控原视频 引入依赖(用于暴露指标) //暴露springboot信息 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-actuator/artifactIdexclusions!-- 去掉springboot默认配置 --exclusiongroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-logging/artifactId/exclusion/exclusions/dependency // 暴露java虚拟机信息dependencygroupIdio.micrometer/groupIdartifactIdmicrometer-registry-prometheus/artifactIdscoperuntime/scope/dependency配置 management:endpoint:metrics:enabled: true #支持metricsprometheus:enabled: true #支持Prometheusmetrics:export:prometheus:enabled: truetags:application: jvm-test #实例名采集endpoints:web:exposure:include: * #开放所有端口 就可以actuator暴露服务bean等信息 堆内存情况对比 内存泄漏的原因: 代码中的内存泄漏 ——可以通过压力测试发现开发中其实并不常见 equals() 和 hashCode() (发生概率小) 在定义新类时没有重写正确的equals()和hashCode()方法。在使用HashMap的场景下 如果使用这个类对象作为keyHashMap在判断key是否已经存在时会使用这些方法如 果重写方式不正确会导致相同的数据被保存多份 public class Demo2 {public static long count 0;public static MapStudent,Long map new HashMap();public static void main(String[] args) throws InterruptedException {while (true){if(count % 100 0){Thread.sleep(10);}Student student new Student();student.setId(1);student.setName(张三);map.put(student,1L);}} } 解决方法 在定义新实体时始终重写equals()和hashCode()方法。 重写时一定要确定使用了唯一标识去区分不同的对象比如用户的id等。 hashmap使用时尽量使用编号id等数据作为key不要将整个实体类对象作为key存放。 内部类引用外部类(发生概率小) 非静态的内部类默认会持有外部类尽管代码上不再使用外部类所以如果有地方引用了这个非静态内部类会导致外部类也被引用垃圾回收时无法回收这个外部类。匿名内部类对象如果在非静态方法中被创建会持有调用者对象垃圾回收时无法回收调用者 public class Outer{private byte[] bytes new byte[1024 * 1024]; //外部类持有数据private static String name 测试;static class Inner{private String name;public Inner() {this.name Outer.name;}}public static void main(String[] args) throws IOException, InterruptedException { // System.in.read();int count 0;ArrayListInner inners new ArrayList();while (true){if(count % 100 0){Thread.sleep(10);}inners.add(new Inner());}} }解决方法 如果不想持有外部类对象应该使用静态内部类使用静态方法可以避免匿名内部类持有调用者对象 ThreadLocal的使用(出现频率较高) 如果仅仅使用手动创建的线程就算没有调用ThreadLocal的remove方法清理数据也不会产生内存泄漏。因为当线程被回收时ThreadLocal也同样被回收。但是如果使用线程池就不一定了 //没有remove就会内存溢出 public class Demo5 {public static ThreadLocalObject threadLocal new ThreadLocal();public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor threadPoolExecutor new ThreadPoolExecutor(Integer.MAX_VALUE, Integer.MAX_VALUE,0, TimeUnit.DAYS, new SynchronousQueue());int count 0;while (true) {System.out.println(count);threadPoolExecutor.execute(() - {threadLocal.set(new byte[1024 * 1024]);threadLocal.remove();});Thread.sleep(10);}} } 线程方法执行完一定要调用ThreadLocal中的remove方法清理对象 String的intern方法(出现极少基本不用intern方法) JDK6中字符串常量池位于堆内存中的Perm Gen永久代中如果不同字符串的intern方法被大量调用字符串常量池会不停的变大超过永久代内存上限之后就会产生内存溢出问题 public class Demo6 {public static void main(String[] args) {while (true){ListString list new ArrayListString();int i 0;while (true) {//String.valueOf(i).intern(); //JDK1.6 perm gen 不会溢出list.add(String.valueOf(i).intern()); //溢出}}} } 不用就行了 通过静态字段保存对象(高频) 如果大量的数据在静态变量中被长期引用数据就不会被释放如果这些数据不再使用就成为了内存泄漏 //懒加载案例 Lazy //懒加载 Component public class TestLazy {private byte[] bytes new byte[1024 * 1024 * 1024]; }//缓存案例 public class CaffineDemo {public static void main(String[] args) throws InterruptedException {CacheObject, Object build Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(100)).build();int count 0;while (true){build.put(count,new byte[1024 * 1024 * 10]);Thread.sleep(100L);}} } 解决方法 尽量减少将对象长时间的保存在静态变量中如果不再使用必须将对象删除比如在集合中或者将静态变量设置为null。使用单例模式时尽量使用懒加载而不是立即加载。Spring的Bean中不要长期存放大对象如果是缓存用于提升性能尽量设置过期时间定期失效 资源没有正常关闭 并发请求问题 并发请求问题指的是用户通过发送请求向Java应用获取数据正常情况下Java应用将数据返回之后这部分数据就可以在内存中被释放掉。但是由于用户的并发请求量有可能很大同时处理数据的时间很长导致大量的数据存在于内存中最终超过了内存的上限导致内存溢出。这类问题的处理思路和内存泄漏类似首先要定位到对象产生的根源。 tips: 默认springboot的tomcat线程池为200只能接收200并发 Jmeter模拟并发请求 ——模拟并发压测接口Apache Jmeter是一款开源的测试软件使用Java语言编写最初是为了测试Web程序目前已经发展成支持数据库、消息队列、邮件协议等不同类型内容的测试工具。 使用Apache Jmeter软件可以进行并发请求测试。Apache Jmeter是一款开源的测试软件使用Java语言编写最初是为了测试Web程序目前已经发展成支 持数据库、消息队列、邮件协议等不同类型内容的测试工具。 Apache Jmeter支持插件扩展生成多样化的测试结果 插件增强(不是必须可能自带) 作用显示: 显示活跃线程数响应时长每分钟的事务数 插件增加3个工具 将插件压缩包的lib目录下jar包放入jmeter下的lib包下 插件压缩包ext下的jar包放入jemeter下的ext包下 案例 背景:小李的团队发现有一个微服务在晚上8点左右用户使用的高峰期会出现内存溢出的问题于是他们希望在自己的开发环境能重现类似的问题步骤: 安装Jmeter软件添加线程组在线程组中增加Http请求添加随机参数在线程组中添加监听器 – 聚合报告用来展示最终结果启动程序运行线程组并观察程序是否出现内存溢出 使用 创建线程组新减http请求 设置合适的线程数 建聚合报告 填写相关配置 4. 选出并创建合适的随机函数生成参数 设置参数 诊断问题 MAT工具使用和内存快照 ——MAT是eclipse自带的一款工具通过配置VM option 内存异出时才能生成异常数据的分析文件配合MAT分析 MAT工具单独下载:(装个jdk11及以上的jdk就行了指定一下路径)https://blog.csdn.net/wts563540/article/details/132380827 当堆内存溢出时需要在堆内存溢出时将整个堆内存保存下来生成内存快照(Heap Profile )文件。生成内存快照的Java虚拟机参数 -XX:HeapDumpOnOutOfMemoryError //发生OutOfMemoryError错误时自动生成hprof内存快照件。 -XX:HeapDumpPathpath //指定hprof文件的输出路径。使用MAT打开hprof文件并选择内存泄漏检测功能MAT会自行根据内存快照中保存的数据分析内存泄漏的根源 Jmap命令 案例: 没有溢出但是持续增长怎么分析——jmap命令 jmap生成快照是会占用资源的会影响用户的使用 背景:小李的团队通过监控系统发现有一个服务内存在持续增长希望尽快通过内存快照分析增长的原因由于并未产生内存溢出所以不能通过HeapDumpOnOutOfMemoryError参数生成内存快照 思路:导出运行中系统的内存快照比较简单的方式有两种注意只需要导出标记为存活的对象 通过JDK自带的jmap命令导出格式为 jmap -dump:live,formatb,file文件路径和文件名 进程ID通过arthas的heapdump命令导出格式为 heapdump --live 文件路径和文件名分析超大堆的内存快照 ——服务器内存有限内存过大分析撑不住的情况下使用 在程序员开发用的机器内存范围之内的快照文件直接使用MAT打开分析即可。但是经常会遇到服务器上的程序占用的内存达到10G以上开发机无法正常打开此类内存快照此时需要下载服务器操作系统对应的MAT。下载地址https://eclipse.dev/mat/downloads.php通过MAT中的脚本生成分析报告 ./ParseHeapDump.sh 快照文件路径 org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components不想官网(官网下载最新只支持jdk11以上)下直接—— 链接https://pan.baidu.com/s/1b4jw1eydPI7srlcXd7IAbA?pwd9y4z 提取码9y4z 使用 上传到服务器打开mat的memoryAnalyzer.ini修改需要的内存大小(默认只有1g撑不住大内存分析) 将文件的权限全部打开 在mat目录下执行导出 ./ParseHeapDump.sh 快照文件路径 org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components下载三份报告 解压打开静态页面 修复问题 内存溢出的问题分为3类 代码中的内存溢出 代码中的内存泄漏在前面的篇章中已经介绍并提供了解决方案 并发引起的内存溢出——参数不当 由于参数设置不当比如堆内存设置过小导致并发量增加之后超过堆内存的上限。解决方案调整参数详细往下看 并发引起内存溢出——设计不当 系统的方案设计不当比如 从数据库获取超大数据量的数据线程池设计不当生产者-消费者模型消费者消费性能问题 解决方案优化设计方案 案例1:分页查询文章接口导致的内存溢出 小李负责的新闻资讯类项目采用了微服务架构其中有一个文章微服务这个微服务在业务高峰期出现了内存溢出的现象背景小李负责的新闻资讯类项目采用了微服务架构其中有一个文章微服务这个微服务在业务高峰期出现了内存溢出的现象。解决思路1、服务出现OOM内存溢出时生成内存快照。2、使用MAT分析内存快照找到内存溢出的对象。3、尝试在开发环境中重现问题分析代码中问题产生的原因。4、修改代码。5、测试并验证结果。 实践 解决思路的12不再赘述 //给需要导出hprof文件的项目启动时的参考参数 java -jar -Dserver.port8081 -Xmx512m -Xms256m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/testproject/learnJvm/jvm.hprof jvm-optimize-0.0.1-SNAPSHOT.jar如何分析hprof文件找到对应的问题接口? 练习文件: 解决思路:打开hprof文件图中有两个地方占用大量内存第一个怀疑对象 tomcat线程池的大量线程导致的内存占用(但是这里看不出太多问题) 第二个怀疑对象 打开支配树点击进行深度排序 如何查看是什么占用内存的是什么数据? 按树点开还可以看到来跟数据表相关的内容标识 如果是线程的问题怎么查看是哪个接口呢? 右键查看当前对象引用的对象: outgoing: 查看当前对象引用的对象ingoing: 查看当前对象被哪些对象引用 然后跳转如下就能找到对应的接口方法 如何根据接口确定是否相关对象占用过大? 假设定位到此接口问题点击直方图输入该对象的包前缀 如何模拟重现问题 定位到问题就在开放环境重现问题比如通过Jemter进行模拟 解决案例1问题 问题根源文章微服务中的分页接口没有限制最大单次访问条数并且单个文章对象占用的内存量较大在业务高峰期并发量较大时这部分从数据库获取到内存之后会占用大量的内存空间解决思路: 与产品设计人员沟通限制最大的单次访问条数。分页接口如果只是为了展示文章列表不需要获取文章内容可以大大减少对象的大小。在高峰期对微服务进行限流保护 案例2: Mybatis导致的内存溢出 背景小李负责的文章微服务进行了升级新增加了一个判断id是否存在的接口第二天业务高峰期再次出现了内存溢出小李觉得应该和新增加的接口有关系。解决思路1、服务出现OOM内存溢出时生成内存快照。2、使用MAT分析内存快照找到内存溢出的对象。3、尝试在开发环境中重现问题分析代码中问题产生的原因。4、修改代码。5、测试并验证结果 实践 打开mat分析hprof文件打开第二个柱状图分析与案例1同理找到溢出接口树状图——handler——ListObject——outgoing 分析SQL相关的内存占用 如图左下角说明mybatis有大量foreach拼接 代码: 通过jemeter进行测试 解决案例2 问题根源Mybatis在使用foreach进行sql拼接时会在内存中创建对象如果foreach处理的数组或者集合元素个数过多会占用大量的内存空间。解决思路1、限制参数中最大的id个数。2、将id缓存到redis或者内存缓存中通过缓存进行校验 案例3:导出大文件内存溢出 背景小李负责了一个管理系统这个管理系统支持几十万条数据的excel文件导出。他发现系统在运行时如果有几十个人同时进行大数据量的导出会出现内存溢出。小李团队使用的是k8s将管理系统部署到了容器中所以这一次我们使用阿里云的k8s环境还原场景并解决问题。阿里云的k8s整体规划如下: 搭建k8s集群 链接 问题根源Excel文件导出如果使用POI的XSSFWorkbook在大数据量几十万的情况下会占用大量的内存。解决思路1、使用poi的SXSSFWorkbook。(最快内存消耗大比原来优化一点点)2、hutool提供的BigExcelWriter减少内存开销。(慢一点内存消耗较小)3、使用easy excel对内存进行了大量的优化(更慢一点分批处理内存优化最好) 案例4:ThreadLocal使用时占用大量内存 背景小李负责了一个微服务但是他发现系统在没有任何用户使用时也占用了大量的内存。导致可以使用的内存大大减少 模拟思路 创建项目部署的服务器jemeter模拟并发请求生成hprof文件下载本地通过MAT分析 问题根源和解决思路很多微服务会选择在拦截器preHandle方法中去解析请求头中的数据并放入一些数据到ThreadLocal中方便后续使用。在拦截器的afterCompletion方法中必须要将ThreadLocal中的数据清理掉。 案例5文章内容审核接口的内存溢出 背景文章微服务中提供了文章审核接口会调用阿里云的内容安全接口进行文章中文字和图片的审核在自测过程中出现内存占用较大的问题 设计1 使用SpringBoot中的Async注解进行异步的审核存在问题1、线程池参数设置不当会导致大量线程的创建或者队列中保存大量的数据。2、任务没有持久化一旦走线程池的拒绝策略或者服务宕机、服务器掉电等情况很有可能会丢失任务。 设计2 使用生产者和消费者模式进行处理队列数据可以实现持久化到数据库存在问题1、队列参数设置不正确会保存大量的数据。2、实现复杂需要自行实现持久化的机制否则数据会丢失 设计3: 使用mq消息队列进行处理由mq来保存文章的数据。发送消息的服务和拉取消息的服务可以是同一个也可以不是同一个。具体实现按项目需求分析:问题根源和解决思路 在项目中如果要使用异步进行业务处理或者实现生产者 – 消费者的模型如果在Java代码中实现会占用大量的内存去保存中间数据。尽量使用Mq消息队列可以很好地将中间数据单独进行保存不会占用Java的内存。同时也可以将生产者和消费者拆分成不同的微服务 两种方式的优缺点 在线定位 如果不想对用户访问产生过多影响采用在线定位但是信息没有快照详细 jmap在线定位步骤 使用jmap -histo:live 进程ID 文件名 命令将内存中存活对象以直方图的形式保存到文件中这个过程会影响用户的时间但是时间比较短暂。 jmap -histo:live 进程ID 文件名分析内存占用最多的对象一般这些对象可能就是造成内存泄漏的原因。 启动服务器上的arthas 使用arthas的stack命令追踪对象创建的方法被调用的调用路径找到对象创建的根源。 即可定位到哪一行进行的创建 btrace在线定位步骤 参考——也可以使用btrace工具编写脚本追踪方法执行的过程。BTrace 是一个在Java 平台上执行的追踪工具可以有效地用于线上运行系统的方法追踪具有侵入性小、对性能的影响微乎其微等特点。 项目中可以使用btrace工具打印出方法被调用的栈信息。这种方式需要有一定经验进行定制化脚本编写能够获取更多的信息。 使用方法1、下载btrace工具 官方地址https://github.com/btraceio/btrace/releases/latest2、编写btrace脚本通常是一个java文件。3、将btrace工具和脚本上传到服务器在服务器上运行 btrace 进程ID 脚本文件名 。4、观察执行结果。 小结 解决内存泄漏问题的方法是什么 发现问题通过监控工具尽可能尽早地发现内存慢慢变大的现象。诊断原因通过分析内存快照或者在线分析方法调用过程诊断问题产生的根源定位到出现问题的源代码。修复源代码中的问题如代码bug、技术方案不合理、业务设计不合理等等。在测试环境验证问题是否已经解决最后发布上线 GC调优 ——最后总结gc调优调什么?怎么调? 学习如何分析GC日志解决生产环境由于频繁Full GC导致的系统假死的问题 GC调优指的是对垃圾回收Garbage Collection进行调优。GC调优的主要目标是避免由垃圾回收引起程序性能下降。 GC调优的核心分成三部分 通用Jvm参数的设置。特定垃圾回收器的Jvm参数的设置。解决由频繁的FULLGC引起的程序性能问题。 GC调优没有唯一的标准答案如何调优与硬件、程序本身、使用情况均有关系重点学习调优的工具和方法 GC调优的指标 吞吐量: 吞吐量分为业务吞吐量和垃圾回收吞吐量——业务吞吐量指的在一段时间内程序需要完成的业务数量。比如企业中对于吞吐量的要求可能会是这样的 支持用户每天生成10000笔订单在晚上8点到10点支持用户查询50000条商品信息 保证高吞吐量的常规手段有两条 优化业务执行性能减少单次业务的执行时间优化垃圾回收吞吐量 垃圾回收吞吐量 垃圾回收吞吐量指 CPU 用于执行用户代码的时间与 CPU 总执行时间的比值即吞吐量 执行用户代码时间 /执行用户代码时间 GC时间。吞吐量数值越高垃圾回收的效率就越高允许更多的CPU时间去处理用户的业务相应的业务吞吐量也就越高 延迟 ——响应时间延迟指的是从用户发起一个请求到收到响应这其中经历的时间。 比如企业中对于延迟的要求可能会是这样的所有的请求必须在5秒内返回给用户结果 延迟 GC延迟 业务执行时间所以如果GC时间过长会影响到用户的使用。 内存的使用量 内存使用量指的是Java应用占用系统内存的最大值一般通过Jvm参数调整在满足上述两个指标的前提下这个值越小越好。 GCeasy工具 ——用于分析垃圾回收的吞吐量等信息 GC调优的方法工具 GC调优的四个步骤 jstat工具 ——重点关注fullgc的次数和时间 FGC、FGCT Jstat工具是JDK自带的一款监控工具可以提供各种垃圾回收、类加载、编译信息等不同的数据。 使用方法为jstat -gc 进程ID 每次统计的间隔毫秒 统计次数 jstat -gc 进程ID 每次统计的间隔毫秒 统计次数组合下面的字母标识上图的指标 C代表Capacity容量U代表Used使用量S – 幸存者区E – 伊甸园区O – 老年代M – 元空间YGC、YGT年轻代GC次数和GC耗时单位秒FGC、FGCTFull GC次数和Full GC耗时GCTGC总耗时 优点 简单 缺点 无法精确到gc产生的时间只能判断gc是否有问题 VisualVM ——只能用于开发环境生产环境会占用CPU资源VisualVm中提供了一款Visual Tool插件实时监控Java进程的堆内存结构、堆内存变化趋势以及垃圾回收时间的变化趋势。同时还可以监控对象晋升的直方图 适合开发使用能直观的看到堆内存和GC的变化趋势对程序运行性能有一定影响生产环境程序员一般没有权限进行操作 VisualGC的GC插件 查看某个进程的堆内存状态 Prometheus Grafana PrometheusGrafana是企业中运维常用的监控方案其中Prometheus用来采集系统或者应用的相关数据同时具备告警功能。Grafana可以将Prometheus采集到的数据以可视化的方式进行展示。 Java程序员要学会如何读懂Grafana展示的Java虚拟机相关的参数 优点 支持系统级别和应用级别的监控比如linux操作系统、Redis、MySQL、Java进程。支持告警并允许自定义告警指标通过邮件、短信等方式尽早通知相关人员进行处理 缺点 环境搭建较为复杂一般由运维人员完成 GC 日志 ——比上述工具更详细的报告用于定位问题通过GC日志可以更好的看到垃圾回收细节上的数据同时也可以根据每款垃圾回收器的不同特点更好地发现存在的问题。 直接在控制台打印信息 -verbose:gc 使用方法JDK 8及以下-XX:PrintGCDetails -Xloggc:文件名 //不能和-verbose:gc 同时使用只能单独使用不指定路径只指定文件名默认保存项目目录下 -XX:PrintGCDetails -Xloggc:保存文件的位置使用方法JDK 9-Xlog:gc*:file文件名 -Xlog:gc*:file文件名效果 GC Viewer ——用于可视化上述的gc日志 GCViewer是一个将GC日志转换成可视化图表的小工具github地址https://github.com/chewiebug/GCViewer使用方法java -jar gcviewer_1.3.4.jar 日志文件.log GC easy ——使用简单的ai分析平台分析gc日志并提供建议需要翻墙 GCeasy是业界首款使用AI机器学习技术在线进行GC分析和诊断的工具。定位内存泄漏、GC延迟高的问题提供JVM参数优化建议支持在线的可视化工具图表展示。官方网站https://gceasy.io/ 以上是所有gc调优相关的工具 常见的GC模式 一、正常情况 特点呈现锯齿状对象创建之后内存上升一旦发生垃圾回收之后下降到底部并且每次下降之后的内存大小接近存留的对象较少。 二、缓存对象过多 特点呈现锯齿状对象创建之后内存上升一旦发生垃圾回收之后下降到底部并且每次下降之后的内存大小接近处于比较高的位置。问题产生原因 程序中保存了大量的缓存对象导致GC之后无法释放可以使用MAT或者HeapHero等工具进行分析内存占用的原因。 三、内存泄漏 特点呈现锯齿状每次垃圾回收之后下降到的内存位置越来越高最后由于垃圾回收无法释放空间导致对象无法分配产生OutOfMemory的错误。问题产生原因 程序中保存了大量的内存泄漏对象导致GC之后无法释放可以使用MAT或者HeapHero等工具进行分析是哪些对象产生了内存泄漏 四、持续的FullGC 特点在某个时间点产生多次Full GCCPU使用率同时飙高用户请求基本无法处理。一段时间之后恢复正常。问题产生原因 在该时间范围请求量激增程序开始生成更多对象同时垃圾收集无法跟上对象创建速率导致持续地在进行FULL GC。GC分析报告 五、元空间不足导致的FULLGC 特点堆内存的大小并不是特别大但是持续发生FULLGC。问题产生原因 元空间大小不足导致持续FULLGC回收元空间的数据。GC分析报告 解决GC问题的手段 解决GC问题的手段中前三种是比较推荐的手段第四种仅在前三种无法解决时选用 如何通过oracle官网找到jdk的相关参数(重要) https://www.oracle.com/products拉倒最下面找到resources获取文档选择对应的版本点击对应版本选择jdk tool找到所以命令选择java当前页面就能找到所有jvm参数(全局搜索想要的参数) 优化基础JVM参数 -Xmx 和 –Xms -Xmx参数设置的是最大堆内存但是由于程序是运行在服务器或者容器上计算可用内存时要将元空间、操作系统、其它软件占用的内存排除掉。案例 服务器内存4G操作系统元空间最大值其它软件占用1.5G-Xmx可以设置为2g。最合理的设置方式应该是根据最大并发量估算服务器的配置然后再根据服务器配置计算最大堆内存的值-Xms用来设置初始堆大小建议将-Xms设置的和-Xmx一样大有以下几点好处 运行时性能更好堆的扩容是需要向操作系统申请内存的这样会导致程序性能短期下降。可用性问题如果在扩容时其他程序正在使用大量内存很容易因为操作系统内存不足分配失败。启动速度更快Oracle官方文档的原话如果初始堆太小Java 应用程序启动会变得很慢因为 JVM 被迫频繁执行垃圾收集直到堆增长到更合理的大小。为了获得最佳启动性能请将初始堆大小设置为与 -XX:MaxMetaspaceSize 和 –XX:MetaspaceSize -XX:MaxMetaspaceSize值 参数指的是最大元空间大小默认值比较大如果出现元空间内存泄漏会让操作系统可用内存不可控建议根据测试情况设置最大值一般设置为256m。设置最大值避免扩容提前抛出内存不足的问题 -XX:MetaspaceSize值 参数指的是到达这个值之后会触发FULLGC网上很多文章的初始元空间大小是错误的后续什么时候再触发JVM会自行计算。如果设置为和MaxMetaspaceSize一样大就不会FULLGC但是对象也无法回收。 -Xss 如果我们不指定栈的大小JVM 将创建一个具有默认大小的栈。大小取决于操作系统和计算机的体系结构。比如Linux x86 64位 1MB如果不需要用到这么大的栈内存完全可以将此值调小节省内存空间合理值为256k – 1m之间。使用-Xss256k 不建议手动设置的参数 由于JVM底层设计极为复杂一个参数的调整也许让某个接口得益但同样有可能影响其他更多接口。 -Xmn 年轻代的大小默认值为整个堆的1/3可以根据峰值流量计算最大的年轻代大小尽量让对象只存放在年轻代不进入老年代。但是实际的场景中接口的响应时间、创建对象的大小、程序内部还会有一些定时任务等不确定因素都会导致这个值的大小并不能仅凭计算得出如果设置该值要进行大量的测试。G1垃圾回收器尽量不要设置该值G1会动态调整年轻代的大小。 ‐XX:SurvivorRatio 伊甸园区和幸存者区的大小比例默认值为8。 ‐XX:MaxTenuringThreshold 最大晋升阈值(默认15)年龄大于此值之后会进入老年代。另外JVM有动态年龄判断机制将年龄从小到大的对象占据的空间加起来如果大于survivor区域的50%然后把等于或大于该年龄的对象放入到老年代。幸存者区: s0和s1 其他参数 -XX:DisableExplicitGC 禁止在代码中使用System.gc() System.gc()可能会引起FULLGC在代码中尽量不要使用。使用DisableExplicitGC参数可以禁止使用System.gc()方法调用。 -XX:HeapDumpOnOutOfMemoryError 发生OutOfMemoryError错误时自动生成hprof内存快照文件。 -XX:HeapDumpPathpath指定hprof文件的输出路径。打印GC日志 JDK8及之前 -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:文件路径 JDK9及之后 -Xlog:gc*:file文件路径JVM参数模板 -Xms1g -Xmx1g -Xss256k -XX:MaxMetaspaceSize512m -XX:DisableExplicitGC -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/opt/logs/my-service.hprof -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:文件路径注意JDK9及之后gc日志输出修改为 -Xlog:gc*:file文件名堆内存大小和栈内存大小根据实际情况灵活调整 垃圾回收器的选择 背景小李负责的程序在高峰期遇到了性能瓶颈团队从业务代码入手优化了多次也取得了不错的效果这次他希望能采用更合理的垃圾回收器优化性能。思路 编写Jmeter脚本对程序进行压测同时添加RT响应时间、每秒钟的事务数等指标进行监控。选择不同的垃圾回收器进行测试并发量分别设置50、100、200观察数据的变化情况。 思路 编写Jmeter脚本对程序进行压测同时添加RT响应时间、每秒钟的事务数等指标进行监控。选择不同的垃圾回收器进行测试并发量分别设置50、100、200观察数据的变化情况。JDK8 下 ParNew CMS 组合(注重吞吐量) -XX:UseParNewGC -XX:UseConcMarkSweepGC 默认组合(注重响应时间) PS PO JDK8使用g1 : -XX:UseG1GCJDK11 默认 g1 优化垃圾回收器的参数 这部分优化效果未必出色仅当前边的一些手动无效时才考虑一个优化的案例CMS的并发模式失败concurrent mode failure现象。由于CMS的垃圾清理线程和用户线程是并行进行的如果在并发清理的过程中老年代的空间不足以容纳放入老年代的对象会产生并发模式失败。 案例 一个优化的案例CMS的并发模式失败concurrent mode failure现象。由于CMS的垃圾清理线程和用户线程是并行进行的如果在并发清理的过程中老年代的空间不足以容纳放入老年代的对象会产生并发模式失败。并发模式失败会导致Java虚拟机使用Serial Old单线程进行FULLGC回收老年代出现长时间的停顿。 解决方案1.减少对象的产生以及对象的晋升。2.增加堆内存大小3.优化垃圾回收器的参数比如-XX:CMSInitiatingOccupancyFraction值当老年代大小到达该阈值时会自动进行CMS垃圾回收通过控制这个参数提前进行老年代的垃圾回收减少其大小。JDK8中默认这个参数值为 -1根据其他几个参数计算出阈值((100 - MinHeapFreeRatio) (double)(CMSTriggerRatio * MinHeapFreeRatio) / 100.0)该参数设置完是不会生效的必须开启-XX:UseCMSInitiatingOccupancyOnly参数。full gc数量并没有减少只是降低了频率 属于锦上添花治标不治本 实战: 内存调优GC调优 背景小李负责的程序在高峰期经常会出现接口调用时间特别长的现象他希望能优化程序的性能。思路 生成GC报告通过Gceasy工具进行分析判断是否存在GC问题或者内存问题。存在内存问题通过jmap或者arthas将堆内存快照保存下来。通过MAT或者在线的heaphero工具分析内存问题的原因。修复问题并发布上线进行测试。 案例参数 java -jar -Xms1g -Xmx1g -Xss256k -XX:MaxMetaspaceSize256m -XX:UseParNewGC -XX:UseConcMarkSweepGC -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPathtest.hprof -XX:PrintGCDateStamps -XX:PrintGCDetails -XX:DisableExplicitGC -Xloggc:test.log perform.jar-Xms1g: 设置JVM初始堆内存大小为1GB。 -Xmx1g: 设置JVM最大堆内存大小为1GB。 -Xss256k: 设置每个线程的堆栈大小为256KB。 -XX:MaxMetaspaceSize256m: 设置元空间用于存放类元数据的最大大小为256MB。 -XX:UseParNewGC: 使用并行新生代垃圾回收器ParNew收集器。 -XX:UseConcMarkSweepGC: 使用并发标记清除垃圾回收器CMS收集器。 -XX:HeapDumpOnOutOfMemoryError: 当出现内存溢出错误时生成堆转储文件。 -XX:HeapDumpPathtest.hprof: 指定堆转储文件的路径为test.hprof。 -XX:PrintGCDateStamps: 在垃圾回收日志中打印GC的日期和时间戳。 -XX:PrintGCDetails: 打印详细的垃圾回收日志信息。 -XX:DisableExplicitGC: 禁用显示的GC调用如System.gc()。 -Xloggc:test.log: 将垃圾回收日志输出到文件test.log。 perform.jar: 要运行的Java程序包JAR文件 问题1发生了连续的FULL GC,堆内存1g如果没有请求的情况下内存大小在200-300mb之间。分析没有请求的情况下内存大小并没有处于很低的情况满足缓存对象过多的情况怀疑内存种缓存了很多数据。需要将堆内存快照保存下来进行分析。 问题2堆内存快照保存到本地之后使用MAT打开发现只有几十兆的内存。分析有大量的对象不在GC Root引用链上可以被回收使用MAT查看这些对象。问题3由于这些对象已经不在引用链上无法通过支配树等手段分析创建的位置。分析在不可达对象列表中除了发现大量的byte[]还发现了大量的线程可以考虑跟踪线程的栈信息来判断对象在哪里创建。问题产生原因在定时任务中通过线程创建了大量的对象导致堆内存一直处于比较高的位置。解决方案暂时先将这段代码注释掉测试效果由于这个服务本身的内存压力比较大将这段定时任务移动到别的服务中。 问题4修复之后内存基本上处于100m左右但是当请求发生时依然有频繁FULL GC的发生。分析请求产生的内存大小比当前最大堆内存大尝试选择配置更高的服务器将-Xmx和-Xms参数调大一些。 案例总结1、压力比较大的服务中尽量不要存放大量的缓存或者定时任务会影响到服务的内存使用。2、内存分析发现有大量线程创建时可以使用导出线程栈来查看线程的运行情况。3、如果请求确实创建了大量的内存超过了内存上限只能考虑减少请求时创建的对象或者使用更大的内存。4、推荐使用g1垃圾回收器并且使用较新的JDK可以获得更好的性能。 GC调优的核心流程 1、监控是否出现连续的FULL GC或者单次GC时间过长。2、诊断并解决一般通过四种途径解决 优化基础JVM参数减少对象的产生更换垃圾回收器优化垃圾回收参数 3、在测试环境验证问题是否已经解决最后发布上线 性能调优 性能优化的步骤总共分为四个步骤其中修复部分要具体问题具体分析且处理方式各不相同。本章中着重学习发现问题和诊断问题的方法目标是准确定位到性能问题的根源应用程序在运行过程中经常会出现性能问题比较常见的性能问题现象是1、通过top命令查看CPU占用率高接近100甚至多核CPU下超过100都是有可能的。应用程序在运行过程中经常会出现性能问题比较常见的性能问题现象是1、通过top命令查看CPU占用率高接近100甚至多核CPU下超过100都是有可能的。2、请求单个服务处理时间特别长多服务使用skywalking等监控系统来判断是哪一个环节性能低下。 应用程序在运行过程中经常会出现性能问题比较常见的性能问题现象是1、通过top命令查看CPU占用率高接近100甚至多核CPU下超过100都是有可能的。2、请求单个服务处理时间特别长多服务使用skywalking等监控系统来判断是哪一个环节性能低下。3、程序启动之后运行正常但是在运行一段时间之后无法处理任何的请求内存和GC正常。 线程转储方式 线程转储Thread Dump提供了对所有运行中的线程当前状态的快照。线程转储可以通过jstack、visualvm等工具获取。其中包含了线程名、优先级、线程ID、线程状态、线程栈信息等等内容可以用来解决CPU占用率高、死锁等问题。线程转储Thread Dump中的几个核心内容 名称 线程名称通过给线程设置合适的名称更容易“见名知意”优先级prio线程的优先级Java IDtidJVM中线程的唯一ID本地 ID (nid)操作系统分配给线程的唯一ID状态线程的状态分为 NEW – 新创建的线程尚未开始执行RUNNABLE –正在运行或准备执行BLOCKED – 等待获取监视器锁以进入或重新进入同步块/方法WAITING – 等待其他线程执行特定操作没有时间限制TIMED_WAITING – 等待其他线程在指定时间内执行特定操作TERMINATED – 已完成执行 栈追踪 显示整个方法的栈帧信息 线程转储的可视化在线分析平台1、 https://jstack.review/2、 https://fastthread.io/ Jstack命令 //查看java进程 jps //查看线程信息保存到xxx.dump jstack 进程号 xxx.tdumpVisualVM 通过可视化按键生成tdump文件 案例 案例1CPU占用率高问题的解决方案 问题监控人员通过prometheus的告警发现CPU占用率一直处于很高的情况通过top命令看到是由于Java程序引起的希望能快速定位到是哪一部分代码导致了性能问题解决思路1。 通过top –c 命令找到CPU占用率高的进程获取它的进程ID 使用top -p 进程ID 单独监控某个进程按H可以查看到所有的线程以及线程对应 的CPU使用率找到CPU使用率特别高的线程。 使用 jstack 进程ID 命令可以查看到所有线程正在执行的栈信息。使用 jstack 进程ID 文件名 保存到文件中方便查看 找到nid线程ID相同的栈信息需要将之前记录下的十进制线程号转换成16进制。通过 printf ‘%x\n’ 线程ID 命令直接获得16进制下的线程ID。 找到栈信息对应的源代码并分析问题产生原因 遗留问题 如果方法中嵌套方法比较多如何确定栈信息中哪一个方法性能较差 案例补充 在定位CPU占用率高的问题时比较需要关注的是状态为RUNNABLE的线程。但实际上有一些线程执行本地方法时并不会消耗CPU而只是在等待。但 JVM 仍然会将它们标识成“RUNNABLE”状态。 案例2: 接口响应时间很长的问题 问题在程序运行过程中发现有几个接口的响应时间特别长需要快速定位到是哪一个方法的代码执行过程中出现了性能问题。解决思路已经确定是某个接口性能出现了问题但是由于方法嵌套比较深需要借助于arthas定位到具体的方法。 Arthas的trace命令 使用arthas的trace命令可以展示出整个方法的调用路径以及每一个方法的执行耗时。命令 trace 类名 方法名 添加 --skipJDKMethod false 参数可以输出JDK核心包中的方法及耗时。添加 ‘#cost 毫秒值’ 参数只会显示耗时超过该毫秒值的调用。添加 –n 数值 参数最多显示该数值条数的数据。所有监控都结束之后输入stop结束监控重置arthas增强的对象。 Arthas的watch命令 在使用trace定位到性能较低的方法之后使用watch命令监控该方法可以获得更为详细的方法信息。命令 watch 类名 方法名 ‘{params, returnObj}’ ‘#cost毫秒值’ -x 2 ‘{params, returnObj}‘ 代表打印参数和返回值。-x 代表打印的结果中如果有嵌套比如对象里有属性最多只展开2层。允许设置的最大值为4。 总结 通过arthas的trace命令首先找到性能较差的具体方法如果访问量比较大建议设置最小的耗时精确的找到耗时比较高的调用。 通过watch命令查看此调用的参数和返回值重点是参数这样就可以在开发环境或者测试环境模拟类似的现象通过debug找到具体的问题根源。 使用stop命令将所有增强的对象恢复。 案例3: 定位偏底层的性能问题 问题有一个接口中使用了for循环向ArrayList中添加数据但是最终发现执行时间比较长需要定位是由于什么原因导致的性能低下。解决思路Arthas提供了性能火焰图的功能可以非常直观地显示所有方法中哪些方法执行时间比较长。 Arthas的profile命令使用arthas的profile命令生成性能监控的火焰图。命令1 profiler start 开始监控方法执行性能命令2 profiler stop --format html 以HTML的方式生成火焰图火焰图中一般找绿色部分Java中栈顶上比较平的部分很可能就是性能的瓶颈。总结 偏底层的性能问题特别是由于JDK中某些方法被大量调用导致的性能低下可以使用火焰图非常直观的找到原因。这个案例中是由于创建ArrayList时没有手动指定容量导致使用默认的容量而在添加对象过程中发生了多次的扩容扩容需要将原来数组中的元素复制到新的数组中消耗了大量的时间。通过火焰图可以看到大量的调用修复完之后节省了20% ~ 50%的时间。 案例4线程被耗尽问题 问题程序在启动运行一段时间之后就无法接受任何请求了。将程序重启之后继续运行依然会出现相同的情况。 解决思路线程耗尽问题一般是由于执行时间过长分析方法分成两步 检测是否有死锁产生无法自动解除的死锁会将线程永远阻塞。如果没有死锁再使用案例1的打印线程栈的方法检测线程正在执行哪个方法一般这些大 量出现的方法就是慢方法。死锁两个或以上的线程因为争夺资源而造成互相等待的现象 死锁问题黑马《JUC并发编程》相关章节。 地址 https://www.bilibili.com/video/BV16J411h7Rd?p115 解决方案线程死锁可以通过三种方法定位问题 jstack -l 进程ID 文件名 将线程栈保存到本地。 在文件中搜索deadlock即可找到死锁位置 开发环境中使用visual vm或者Jconsole工具都可以检测出死锁。使用线程快照生成工具 就可以看到死锁的根源。生产环境的服务一般不会允许使用这两种工具连接。 使用fastthread自动检测线程问题。 https://fastthread.io/ Fastthread和Gceasy类似是一款在线的AI自动线程问题检测工具可以提供线程分析报告。通过报告查看是否存在死锁问题。 更精细的性能测试 你是如何判断一个方法需要耗时多少时间的 我会在方法上打印开始时间和结束时间他们的差值就是方法的执行耗时。手动通过postman或者jmeter发起一笔请求在控制台上看输出的时间。这样做是不准确的第一测试时有些对象创建是懒加载的所以会影响第一次的请求时间第二因为虚拟机中JIT即时编译器会优化你的代码所以你这个测试得出的时间并不一定是最终用户处理的时间。 JIT对程序性能的影响 Java程序在运行过程中JIT即时编译器会实时对代码进行性能优化所以仅凭少量的测试是无法真实反应运行系统最终给用户提供的性能。如下图随着执行次数的增加程序性能会逐渐优化。 正确地测试代码性能 OpenJDK中提供了一款叫JMHJava Microbenchmark Harness的工具可以准确地对Java代码进行基准测试量化方法的执行性能。官网地址https://github.com/openjdk/jmhJMH会首先执行预热过程确保JIT对代码进行优化之后再进行真正的迭代测试最后输出测试的结果。 JMH环境搭建 创建基准测试项目在CMD窗口中使用以下命令创建JMH环境项目 $ mvn archetype:generate \ -DinteractiveModefalse \ -DarchetypeGroupIdorg.openjdk.jmh \ -DarchetypeArtifactIdjmh-java-benchmark-archetype \ -DgroupIdorg.sample \ -DartifactIdtest \ -Dversion1.0修改POM文件中的JDK版本号和JMH版本号JMH最新版本号参考Github。 编写测试方法几个需要注意的点 死代码问题黑洞的用法 通过maven的verify命令检测代码问题并打包成jar包。 通过java -jar target/benchmarks.jar 命令执行基准测试 java -jar target/benchmarks.jar测试结果通过https://jmh.morethan.io/生成可视化的结果 案例:日期格式化方法性能测试 问题在JDK8中可以使用Date进行日期的格式化也可以使用LocalDateTime进行格式化使用JMH对比这两种格式化的性能 解决思路1、搭建JMH测试环境。2、编写JMH测试代码。3、进行测试。4、比对测试结果。 总结日期格式化方法性能测试1、Date对象使用的SimpleDateFormatter是线程不安全的所以每次需要重新创建对象或者将对象放入ThreadLocal中进行保存。其中每次重新创建对象性能比较差将对象放入ThreadLocal之后性能相对还是比较好的。2、LocalDateTime对象使用的DateTimeFormatter线程安全并且性能较好如果能将DateTimeFormatter对象保存下来性能可以得到进一步的提升。 性能调优综合实战 问题小李的项目中有一个获取用户信息的接口性能比较差他希望能对这个接口在代码中进行彻底的优化提升性能。 解决思路1、使用trace分析性能瓶颈。2、优化代码反复使用trace测试性能提升的情况。3、使用JMH在SpringBoot环境中进行测试。4、比对测试结果。 10000数据量下平均执行时间从原来的217ms优化至2ms。100000数据量下平均执行时间从原来的57秒优化至18ms。总结 本案例中性能问题产生的原因是两层for循环导致的循环次数过多处理时间在循环次数变大的情况下变得非常长考虑将一层循环拆出去创建HashMap用来查询提升性能。 使用LocalDateTime替代SimpleDateFormat进行日期的格式化。 使用stream流改造代码这一步可能会导致性能下降主要是为了第四次优化准备。 使用并行流利用多核CPU的优势并行执行提升性能。 总结 问题1CPU占用率高的问题解决方案通过top命令找到进程和线程 - 使用jstack打印线程快照 - 找到线程快照正在执行的方法 并优化性能。 问题2接口响应时间特别长解决方案1通过arthas的trace和watch命令监控方法的执行耗时和参数、返回值等信息定位性能瓶颈并优化性能。解决方案2通过arthas的profile火焰图功能找到火焰图中顶部较平的方法一般就是性能问题产生的根源并优化性能。 问题3线程不可用问题解决方案通过jstack 、visualvm 、fastthread.io等工具找到线程死锁的原因解决死锁问题。 如何判断一个方法需要耗时多少时间的? 我们使用了OpenJDK中的jmh基准测试框架对某些特定的方法比如加密算法进行基准测试jmh可以完全模拟运行环境中的Java虚拟机参数同时支持预热能通过JIT执行优化后的代码获得更为准确的数据。
http://www.dnsts.com.cn/news/155512.html

相关文章:

  • 便宜的广州网站建设服务最全的wordpress 中文手册
  • 网站建设书籍在线阅读synology做网站
  • 如何快速提高网站权重免费建网站 步骤
  • 做网站推广要注意的事项网站是如何建设的
  • 怎么将网站关键词排名首页石家庄刚刚发生的事
  • 打电话沟通做网站话术怎么删除网站死链
  • 网站建设专业用语如何建设一个收费的影视图文网站
  • 宿迁建设安全监督站网站网站策划方案如何做
  • 天津营销类网站设计网站域名到期登不上是怎么回事
  • 物流网站购买物流单号广告公司的网站建设
  • 郑州电商网站建设莱芜一中谭苗苗事件
  • 做网站专业公司兰州市生态建设管理局网站
  • 网站策划的工作要求怎么建设网站服务器
  • 服装设计参考网站网站方案报价
  • 北京做网站的公司排名大神自己做的下载音乐的网站
  • asp+sql server典型网站建设案例大数据开发平台
  • 一个云主机可以做多少网站网站说服力-营销型网站策划
  • 做网站会员功能做网站卖东西赚钱么
  • 嘉兴北京网站建设中融木业网站是那个网站做的
  • 做非洲国际贸易网站网站图片规格
  • 威宁住房和城乡建设局网站南开天津网站建设
  • 服装网站建设风格wordpress版本可以恢复旧版本
  • 万网买的网站备案吗网上服装商城网站代码
  • 茄子河区网站建设自查报告上海企业倒闭
  • 雷神代刷网站推广wordpress 多语言设置
  • 公司开发个网站ip营销的概念
  • 曹县网站开发公司百度关键词搜索推广
  • 南京公司网站制作教育培训网页模板网站
  • html网页制作个人网站如何建设网站简介
  • 吉林智能网站建设价格下载软件的网站