首页 文章 精选 留言 我的

精选列表

搜索[优化],共10000篇文章
优秀的个人博客,低调大师

一次线上JVM调优实践,FullGC40次/天到10天一次的优化过程

在高并发下,Java程序的GC问题属于很典型的一类问题,带来的影响往往会被进一步放大。不管是「GC频率过快」还是「GC耗时太长」,由于GC期间都存在Stop The World问题,因此很容易导致服务超时,引发性能问题。 YGC耗时过长的排查与解决案例 我们的广告服务在新版本上线后,收到了大量的服务超时告警,通过下面的监控图可以看到:超时量突然大面积增加,1分钟内甚至达到了上千次接口超时。下面详细介绍下该问题的排查过程。 1.检查监控 收到告警后,我们第一时间查看了监控系统,立马发现了YoungGC耗时过长的异常。我们的程序大概在21点50左右上线,通过下图可以看出:在上线之前,YGC基本几十毫秒内完成,而上线后YGC耗时明显变长,最长甚至达到了3秒多。 由于YGC期间程序会Stop The World,而我们上游系统设置的服务超时时间都在几百毫秒,因此推断:是因为YGC耗时过长引发了服务大面积超时。 按照GC问题的常规排查流程,我们立刻摘掉了一个节点,然后通过以下命令dump了堆内存文件用来保留现场。 jmap -dump:format=b,file=heap pid 最后对线上服务做了回滚处理,回滚后服务立马恢复了正常,接下来就是长达1天的问题排查和修复过程。 2.确认JVM配置 用下面的命令,我们再次检查了JVM的参数 可以看到堆内存为4G,新生代和老年代均为2G,新生代采用ParNew收集器。 再通过命令 jmap -heap pid 查到:新生代的Eden区为1.6G,S0和S1区均为0.2G。 本次上线并未修改JVM相关的任何参数,同时我们服务的请求量基本和往常持平。因此猜测:此问题大概率和上线的代码相关。 3.代码检查 再回到YGC的原理来思考这个问题,一次YGC的过程主要包括以下两个步骤: 从GC Root扫描对象,对存活对象进行标注 将存活对象复制到S1区或者晋升到Old区 根据下面的监控图可以看出:正常情况下,Survivor区的使用率一直维持在很低的水平(大概30M左右),但是上线后,Survivor区的使用率开始波动,最多的时候快占满0.2G了。而且,YGC耗时和Survivor区的使用率基本成正相关。因此,我们推测:应该是长生命周期的对象越来越多,导致标注和复制过程的耗时增加。 再回到服务的整体表现:上游流量并没有出现明显变化,正常情况下,核心接口的响应时间也基本在200ms以内,YGC的频率大概每8秒进行1次。 很显然,对于局部变量来说,在每次YGC后就能够马上被回收了。那为什么还会有如此多的对象在YGC后存活下来呢? 我们进一步将怀疑对象锁定在:程序的全局变量或者类静态变量上。但是diff了本次上线的代码,我们并未发现代码中有引入此类变量。 4.对dump的堆内存文件进行分析 代码排查没有进展后,我们开始从堆内存文件中寻找线索,使用MAT工具导入了第1步dump出来的堆文件后,然后通过Dominator Tree视图查看到了当前堆中的所有大对象。 立马发现NewOldMappingService这个类所占的空间很大,通过代码定位到:这个类位于第三方的client包中,由我们公司的商品团队提供,用于实现新旧类目转换(最近商品团队在对类目体系进行改造,为了兼容旧业务,需要进行新旧类目映射)。 进一步查看代码,发现这个类中存在大量的静态HashMap,用于缓存新旧类目转换时需要用到的各种数据,以减少RPC调用,提高转换性能。 原本以为,非常接近问题的真相了,但是深入排查发现:这个类的所有静态变量全部在类加载时就初始化完数据了,虽然会占到100多M的内存,但是之后基本不会再新增数据。并且,这个类早在3月份就上线使用了,client包的版本也一直没变过。 经过上面种种分析,这个类的静态HashMap会一直存活,经过多轮YGC后,最终晋升到老年代中,它不应该是YGC持续耗时过长的原因。因此,我们暂时排除了这个可疑点。 5.分析YGC处理Reference的耗时 团队对于YGC问题的排查经验很少,不知道再往下该如何分析了。基本扫光了网上可查到的所有案例,发现原因集中在这两类上: 对存活对象标注时间过长:比如重载了Object类的Finalize方法,导致标注Final Reference耗时过长;或者String.intern方法使用不当,导致YGC扫描StringTable时间过长。 长周期对象积累过多:比如本地缓存使用不当,积累了太多存活对象;或者锁竞争严重导致线程阻塞,局部变量的生命周期变长。 针对第1类问题,可以通过以下参数显示GC处理Reference的耗时-XX:+PrintReferenceGC。添加此参数后,可以看到不同类型的 reference 处理耗时都很短,因此又排除了此项因素。 6.再回到长周期对象进行分析 再往后,我们添加了各种GC参数试图寻找线索都没有结果,似乎要黔驴技穷,没有思路了。综合监控和种种分析来看:应该只有长周期对象才会引发我们这个问题。 折腾了好几个小时,最终峰回路转,一个小伙伴重新从MAT堆内存中找到了第二个怀疑点。 从上面的截图可以看到:大对象中排在第3位的ConfigService类进入了我们的视野,该类的一个ArrayList变量中竟然包含了270W个对象,而且大部分都是相同的元素。 ConfigService这个类在第三方Apollo的包中,不过源代码被公司架构部进行了二次改造,通过代码可以看出:问题出在了第11行,每次调用getConfig方法时都会往List中添加元素,并且未做去重处理。 我们的广告服务在apollo中存储了大量的广告策略配置,而且大部分请求都会调用ConfigService的getConfig方法来获取配置,因此会不断地往静态变量namespaces中添加新对象,从而引发此问题。 至此,整个问题终于水落石出了。这个BUG是因为架构部在对apollo client包进行定制化开发时不小心引入的,很显然没有经过仔细测试,并且刚好在我们上线前一天发布到了中央仓库中,而公司基础组件库的版本是通过super-pom方式统一维护的,业务无感知。 7.解决方案 为了快速验证YGC耗时过长是因为此问题导致的,我们在一台服务器上直接用旧版本的apollo client 包进行了替换,然后重启了服务,观察了将近20分钟,YGC恢复正常。 最后,我们通知架构部修复BUG,重新发布了super-pom,彻底解决了这个问题。 往期推荐 内存溢出及解决方案 JVM调优实战:解决CMS concurrent-abortable-preclean LongGC的问题 四分钟解读JVM垃圾回收算法 一次年轻代GC长暂停问题的解决与思考 解读ThreadLocal的实现原理及应用场景 Redis缓存被污染了,该怎么办? 本文分享自微信公众号 - 码农架构(iByteCoding)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

Android官方开发文档Training系列课程中文版:电池续航时间优化之监测电池电量及充电状态

原文地址:http://android.xsoftlab.net/training/monitoring-device-state/index.html 引言 作为一款优秀的APP应用,应该总是想方设法的降低电量的消耗。通过这节课的学习,你将有能力使APP可以基于设备的状态来调整APP的功能及行为。 我们可以通过比如在断开连接时关闭后台服务,或者在电量低的时候降低更新的频率等等手段来降低电量的消耗。 监测电池电量及充电状态 在更改后台的更新频次时,检查当前的电池电量及充电状态是我们先要做的。 应用程序的更新频率取决于电池的电量以及充电状态。由于设备处于充电状态时应用的耗电量几乎可以忽略,所以,在设备连接到充电器时,你可以将应用的刷新频率开到最大,如果设备没有在充电,那么降低更新频率可以延长电池的使命时间。 检查当前的充电状态 首先我们需要检查当前的充电状态。BatteryManager会将电池信息以及充电信息通过粘性Intent将其广播。 因为是粘性Intent,所以不需要注册BroadcastReceiver,只需要在调用registerReceiver()时传一个null就可以,当前的电池状态由该方法直接返回。你也可以在这里传递一个BroadcastReceiver对象,但是我们接下来的处理方式并不是在其中做的,所以这并不是必须的。 IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = context.registerReceiver(null, ifilter); 如果设备当前处于充电状态,那么可以获得当前的充电状态,无论它是通过USB还是通过AC适配器充电的。 // Are we charging / charged? int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; // How are we charging? int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC; 通常的做法是:应当是在连接到AC电源适配器时,将后台的更新频率加到最大,如果当前处于USB状态,这个频率应当适当降低,如果断开充电,则应当进一步降低。 监测充电状态的变化 设备的充电状态很容易随着充电器的插入、拔出而发生变化。所以随着充电状态的变化应当相应的调整应用的刷新频率。 当设备插上充电器或是拔出充电器时,BatteryManager都会广播一个Action,所以应当注册一个BroadcastReceiver用来监听这些事件。在清单文件中需要定义ACTION_POWER_CONNECTED及ACTION_POWER_DISCONNECTED的意图过滤器。 <receiver android:name=".PowerConnectionReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> </intent-filter> </receiver> 在该BroadcastReceiver内,你可以获取当前的充电状态: public class PowerConnectionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC; } } 检查电池的剩余电量 在一些情况下还需要检查设备的剩余电量。当电量较低时可能需要降低应用的后台服务频率。 你可以通过以下方式获得设备的剩余电量: int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1); float batteryPct = level / (float)scale; 监测电量的重要变化 应用不能一直连续不断的监听电池的状态。 通常来说,一直不断的监听电池电量会使监听电池的任务大于应用的实际任务,所以最好是只监听一些比较重要的变更事件。 下面的清单文件摘自一段广播接收器内。该广播接收器会在电池的电量很低时或者是在电量恢复到安全水平时被触发。它监听了两个事件:ACTION_BATTERY_LOW及ACTION_BATTERY_OKAY. <receiver android:name=".BatteryLevelReceiver"> <intent-filter> <action android:name="android.intent.action.ACTION_BATTERY_LOW"/> <action android:name="android.intent.action.ACTION_BATTERY_OKAY"/> </intent-filter> </receiver> 通常情况下,在电量很低时要关闭所有的后台更新。加载在使用APP之前,手机关机了,那么应用的数据是否是最新的就没那么重要了。 在很多情况下,手机充电时是被放在一个固定的位置上的。下节课我们将会学习如何检查设备的放置环境以及如何监测设备的放置状态。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册