应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

JVM成神路之性能调优篇GC调优Arthas工具详解及线上最

  引言
  在当前的互联网开发模式下,系统访问量日涨、并发暴增、线上瓶颈等各种性能问题纷涌而至,性能优化成为了现时代开发过程中炙手可热的名词,无论是在开发、面试过程中,性能优化都是一个常谈常新的话题。Java语言作为企业应用中的抗鼎者,Java生态中也积攒了大量宝贵的性能优化经验。
  在应用系统中,性能优化其实可以从各个角度出发考虑,如架构优化、前端调优、中间件调优、网关调优、容器调优、JVM调优、接口调优、服务器调优、数据库调优等,从优化类型上而言,主体可以分为三类:结构架构优化:优化应用系统整体架构做到性能提升的目的。如:读写分离、集群热备、分布式架构、引入缓存消息搜索中间件、分库分表、中台架构(大数据中台、基础设施中台)等。配置参数优化:调整应用系统中各层面的配置文件、启动参数达到优化性能的目标。如:JVM、服务器、数据库、、操作系统、中间件、容器、网关参数调整等。代码操作优化:开发者编写程序时,从代码、操作方面进行调节,达到效率更高的初衷。如:代码中使用更优秀的算法思想设计模式、SQL优化、对中间件的操作优化等。
  本章则重点阐述Java中,JVM虚拟机相关的全面优化,如:内存、GC、即时编译、JVM参数配置等。一、系统中性能优化的核心思维
  建立在经验的基础之上才能做好的,对于调优要实事求是,任何的调优手段或技巧不要纸上谈兵,只有经过实践的才能用于生产环境,千万不要将一些没有实际依据的调优策略用于线上环境,否则可能会导致原本好好的程序反而调优调崩溃。1。1、单个节点层面调优的核心思想
  在一个程序中,所有的业务执行实体都为线程,应用程序的性能跟线程是直接挂钩的。而程序中的一条线程必须要经过CPU的调度才可执行,线程执行时必然也会需要数据、产生数据,最终也会和内存、磁盘打交道。因而单个节点的性能表现,不可避免的会跟CPU、内存、磁盘沾上关系。
  线程越多,需要的CPU调度能力也就越强,需要的内存也越大,磁盘IO速率也会要求越快。因此CPU、内存、磁盘,这三者之间的任意之一达到了瓶颈,程序中的线程数量也会达到极限。达到极限后,系统的性能会成抛物线式下滑,从而可能导致系统整体性能下降乃至瘫痪。
  由于如上原因,在考虑性能优化时,必然不能让CPU、内存、磁盘等资源的使用率达到95,一般而言,最大利用率控制在8085左右的最佳状态。
  同时,前面也分析过,因为程序的性能跟线程挂钩,所以线程的模型也是影响性能的重要因素。目前程序设计中主要存在三种线程处理模型:BIO、NIO、AIO(NIO2),BIO是Java中传统的线程一对一处理模型,NIO的最佳实践为reactor模型,而proactor模型又作为了NIO2AIO的落地者。绝大部分情况下,AIO的性能优于NIO,而NIO的性能又远超于BIO。
  所以在做性能优化时,你应该要清楚系统的性能瓶颈在哪儿,到底是要调哪个位置?是线程模型?或是CPU调度?还是内存回收?亦是磁盘IO速率?针对不同层面有不同的优化方案,并非为了追求热词潮流而盲目的调优。1。2、优秀且适用的系统架构胜过千万次调优
  一个单体架构(TomcatMySQL)部署的系统遇到性能问题时,能力再强,本事再大,任凭使出浑身解数也无法将其调到处理万级并发的程序,正常服务器部署的一台MySQL服务做到极致调优也难以在一秒内承载5000的QPS。一味地追求极致的优化,其实也难以解决真正大流量下的并发冲击,因此一套优秀的系统架构胜过自己千万次的调优。
  当然,也并非说项目实现时,越多的技术加进来越好,一套完善的分布式架构就必然比单体架构要好吗?其实也不见得,因为当引入的技术越多,所需要考虑的问题也会更多,耗费的成本也会越高,一个项目收益60W,结果用上最好的配置(高端的开发者顶级的服务器完善的分布式架构)成本耗费200W,这值得吗?答案显而易见。因此,并没有最好的技术架构,只有最适用的架构,能从现有环境及实际业务出发,选用最为合适的技术体系,这才是我们应该做的事情。如:项目业务中读写参半,单节点难以承载压力,项目集群、双主热备值得参考。项目业务中写大于读,引入消息中间件、DB分库、项目集群也可以考虑。项目业务中读大于写,引入缓存搜索中间件、动静分离、读写分离是些不错的选择。。。。。。。。
  当你的系统原有架构遇到性能瓶颈时,你甚至可以考虑进一步做架构优化,如:设计多级分布式缓存、缓存中间件做集群、消息中间件做集群、Java程序做集群、数据库做分库分表、搜索中间件做集群。。。。。,慢慢的,你的系统会越来越庞大复杂,需要处理的问题也更为棘手,但带来的效果也显而易见,随着系统的结构不断变化,承载百万级、千万级、亿级、乃至更大级别的流量也并非难事。
  但只有当你的业务流量访问压力在选用其他架构无法承载时,你才应该考虑更为庞大的架构。当然,如果项目在起步初期就有预估会承载巨大的流量压力,那么提前考虑也很在理,采用分布式微服务架构也并非失策,因为对比其他架构体系而言,微服务架构的拓展性更为灵活。但也需要记住:分布式微服务体系是很好,但它不一定适用于你的项目。1。3、预防大于一切,调优并非临时抱佛脚
  当问题出现时再想办法解决,这种策略永远都属于下下策,防范于未然才是最佳方案,提前防范问题出现主要可分为两个阶段:项目初期预测未来的流量压力,提前根据业务设计出合适的架构,确保上线后可以承载业务的正常增长。项目上线后,配备完善的监控系统,在性能瓶颈来临前设好警报线,确保能够在真正的性能瓶颈到来之前解决问题。
  对于项目初期的架构思考,值得牢记的一点是:不要卡点设计,也不能过度设计造成性能过剩,举例:
  项目上线后的正常情况下,流量大概在一木桶左右,结果你设计时直接整出个池塘级别的结构出来了,这显然是不合理的,毕竟架构体系越庞大,项目的成本也自然就越高。
  当然,也不能说正常情况下压力在一木桶左右,就只设计出一套仅能够承载一木桶流量的结构,这种卡点设计的策略也是不可取的,因为你需要适当考虑业务增长带来的风险,如果卡点设计,那么很容易让项目上线后,短期内就遭遇性能瓶颈。
  因此,如果项目正常的访问压力大概在桶级别,那将结构设计到缸级别是合理的,这样即不必担心过度设计带来的性能过剩,导致成本增高;也无需考虑卡点设计造成的:项目短期遭遇性能瓶颈。
  但设计时的这个度,必须由你自己根据项目的业务场景和环境去思量,不存在前篇一律的方法可教。
  有人曾说过:如果你可以根据业务情景设计出一套能确保业务增长,且在线上能稳定运行三年时间以上的结构,那你就是位业内的顶尖架构,但老话说的好:计划永远赶不上变化,就算思考到业务的每个细节,也不可能设计出一套一劳永逸的结构出现,我们永远无法判断意外和明天哪个先来。因而,项目上线后,配备完善的监控警报系统也是必不可少的。不过值得注意的是:
  监控系统的作用并不是用来提醒你项目嗝屁了的,而是用来提醒你:线上部署的应用系统可能会嗝屁或快嗝屁了,毕竟当项目灾难已经发生时再给警报,那到时候的情况就是:亡羊补牢,为时已晚。
  通常情况下,在监控系统上面设置的性能阈值都会比最大极限值要低515,如:最大极限值是85,那设置告警值一般是75左右就会告警,不会真达到85才告警,只有这样做才能留有足够的时间让运维和开发人员介入排查。当系统发出可能嗝屁的警告时,开发和运维人员就应当立即排查相关的故障隐患,然后再通过不断地修改和优化,提前将可能会出现的性能瓶颈解决,这才是性能调优的正确方案。
  因此,最终结论为:绝不能等到系统奔溃才去优化,预防胜于一切。1。4、无需追求完美,理性权衡利弊
  追求极致,做到完美这点是大部分开发者的通病,很多人会因为这个思想导致自己在面临一些问题时束手无策,比如举个例子:
  业务:MacBookPro一元购活动,预计访问压力:10000QPS。
  环境:单台机器只能承载2000QPS,目前机房中还剩余两台空闲服务器。
  状况:此时就算将空闲的两台机器加上去,也无法顶住目前的访问压力。
  此时你会怎么做?很多人都会茫然,这看起来好像是没办法的事情呀,似乎只能等死了。。。。。
  但事实真的如此吗?并非如此,其实这种情况也有多种解决方案,如:停掉系统中部分非核心的业务,将服务器资源暂时让给该业务。抛弃掉部分用户的请求,只接受处理部分用户的请求。。。。。。。。。
  这些方案是不是可以解决上面的哪个问题呢?答案是肯定的。但完美主义者会认为:
  系统中的服务不能停啊,得保持正常服务啊。
  用户的请求怎么能抛,用户的访问必须得响应啊。
  但事实告诉你的是:类似于京东、淘宝、12306等这些国内的顶级大厂,也照样是这么干的。好比阿里,在双十一的时候都会抽调很多冷门业务的服务器资源给淘宝使用,也包括你在参与这些电商平台的抢购或秒杀类活动时,你是否遇到过如下情况:服务器繁忙,请稍后重试。。。。。。服务器已满,排队中。。。。。前方拥堵,排队中,当前第x位。。。。。
  如果当你遇到了这些情况,答案显而易见,你的请求压根就没有到后端,在前端就给你pass了,然后给你返回了一个字符串,让你傻傻的等待。
  这个例子要告诉大家的是:在处理棘手问题或优化性能时,无需刻意追求完美,理性权衡利弊后,适当地做出一些决断,抛弃掉一部分不重要的,起码比整个系统挂掉要好,何况之后照样也可以恢复。1。5、性能调优的通核心步骤
  性能优化永远是建立在性能瓶颈之上的,如果你的系统没有出现瓶颈,那则无需调优,调优之前需要牢记的一点是:不要为了调优而调优,而是需要调优时才调。
  而发现性能瓶颈的方式有两种,一种是你的应用中具备完善的监控系统,能够提前感知性能瓶颈的出现。另一种则是:应用中没有搭载监控系统,性能瓶颈已经发生,从而导致应用频繁宕机。大型的系统一般都会搭载完善的监控系统,但大多数中小型项目却不具备该条件,因此,大部分中小型项目发现性能瓶颈时,大多数情况下已经嗝屁了。
  通常而言,性能优化的步骤可分为如下几步:发现性能瓶颈:如有监控系统,那它会主动发出警报;如若没有,那出现瓶颈时应用肯定会出问题,如:无响应、响应缓慢、频繁宕机等。排查瓶颈原因:排查瓶颈是由于故障问题导致的,还是真的存在性能瓶颈。定位瓶颈位置:往往一个系统都会由多个层面协同工作,然后对外提供服务,当发现性能瓶颈时,应当确定瓶颈的范围,如:网络带宽瓶颈、Java应用瓶颈、数据库瓶颈等。解决性能瓶颈:定位到具体的瓶颈后对症下药,从结构、配置、操作等方面出发,着手解决瓶颈问题。
  本章则重点是阐述Java虚拟机JVM相关的调优操作,但需要先提前说明的是:
  单层面的性能调优其实只能当成锦上添花的作用,但绝对不能成为系统性能高低、响应快慢、吞吐量大小的决定性要素。应用系统的性能本身就还算可以,那么调优的作用是让其性能更佳。但如若项目结构本身就存在问题,那么能够带来的性能提升也是有限的,如果你想让你的项目快到飞起,那么还需要从多个层面共同着手才能达到目的。二、JVM垃圾收集相关调优策略
  在JVM垃圾收集相关的调优实践中,通常都是以最优吞吐量和最短停顿时间来评价JVM的性能:吞吐量越高代表性能越好、暂停时间越短也代表越好。那么如何做到这两点呢?核心思想在于:尽可能让对象在新生代中分配和回收。尽量避免过多对象进入年老代,缩短年老代GC时间。尽量给JVM分配足够多的内存,减少所有区域中的GC次数。
  归根结底,本质思想就一点:尽量让Java中的对象去到它自己该去的位置,短命的对象就老老实实的进入新生代区域,大对象和长命的对象则进入年老代空间,避免JVM因为对象乱窜导致GC频发和GC时间变长,如:本该在新生代的短命对象由于特殊原因进了年老代,导致年老代GC次数变多时间变长。本该直接分配在年老代的长命大对象,因为某些原因全部被分配在新生代,导致新生代可分配空间变少,引发分配担保机制,造成大量未达到标准的新生代对象提前进入年老代。
  因此,GC调优的目的就相当于给JVM做保养,让其每个区域按照设计的初衷正常工作。
  通常情况下,当JVM存在性能问题时,都会牵扯到两个概念,分配速率(AllocationRate)和提升速率(PromotionRate),这也是分析性能问题时常用的两个指标,其中分配速率影响新生代的垃圾回收,提升速率影响年老代的垃圾回收。2。1、新生代分配速率(AllocationRate)
  分配速率代表固定时间内分配的内存量,通常情况下以MBS为单位,分配速率高,其实并不是什么好事,对于这点我们稍后再做阐述。先来具体如何计算分配的速率。2。1。1、分配速率如何计算?
  一般而言可以通过GC日志计算出来,比如:0。751:〔GC(AllocationFailure)〔PSYoungGen:30705K5115K(38400K)〕30705K12385K(125952K),0。0187498secs〕〔Times:user0。00sys0。00,real0。02secs〕1。514:〔GC(AllocationFailure)〔PSYoungGen:38395K5120K(71680K)〕45665K35687K(159232K),0。0570688secs〕〔Times:user0。09sys0。00,real0。06secs〕3。018:〔GC(AllocationFailure)〔PSYoungGen:70326K5104K(71680K)〕108940K105240K(172032K),0。0866792secs〕〔Times:user0。30sys0。02,real0。09secs〕复制代码
  分配速率计算公式:(本轮GC前使用容量上轮GC后使用容量)(本轮GC时间上轮GC时间)
  GC轮数
  时间差值
  上轮GC后容量
  本轮GC前容量
  容量差值
  分配速率
  第一轮
  751ms
  0KB
  30705KB
  30705KB
  41MBS
  第二轮
  763ms
  5115KB
  38395KB
  33280KB
  44MBS
  第三轮
  1504ms
  5120KB
  70326KB
  65206KB
  43MBS
  每轮均速
  NULL
  NULL
  NULL
  NULL
  43MBS
  通过GC日志中的信息可以初步计算出,该Java程序中的对象分配速率大概在43MBS左右。2。1。2、分配速率对JVM的影响
  前面曾提及过,分配速率高并不是好事,为什么这么说呢?因为Java程序的分配速率越高时,也代表着堆中分配的对象会越多,对象越多也就会让GC的频率更频繁。因此,当分配速率越高,会导致JVM的GC开销越大,分配速率的变化会增加或降低STW的频率,从而影响吞吐量。
  但高分配速率的标准是相对而言的,要根据具体的Eden区大小来判断,一个堆大小为32GB的分配速率是1000MBS,一个500MB的堆空间分配速率为100MBS,前者可被称为是高分配速率吗?并非如此,因为前者的堆有32G,1000MBS的速率也需要一段时间才能触发GC,但后者100MBS的速率对于500M的堆空间而言,则可被称为高速率,因为对于500MB的堆空间而言,会在极短的时间内触发GC。因此,分配速率高低是要根据实际的堆大小来判断。2。1。3、分配速率的四种状况分配速率低,回收速率超于分配速率,GC状态无异常,代表系统GC正常。分配速率高,回收速率略低于或远低于分配速率,代表程序存在OOM隐患。分配速率高,但回收速率勉强可以跟上,代表系统处于亚健康状态。分配速率低,GC次数频繁,释放空间较少,可能存在内存泄漏。
  其中为正常状况,无需做任何处理,也没必要去对于这类系统做刻意优化,如果你的Java应用的JVM处于该状态,但程序整体吞吐量依旧上不去,或响应速度缓慢,那应该从其他层面入手解决。
  如果Java应用出现第种情况,其实应用本身是没有任何问题的,这种情况一般是由于分配的堆空间不足,分配速率过快,导致频繁触发GC回收阈值,因此造成GC负载过重,对于这类情况应该适当调大堆空间,从而使GC频繁下降。
  、则都是程序中存在隐患会出现的状况,通常情况下都是由于程序中存在不规范的代码导致的。状况是因为代码在堆中生成了大量对象,造成分配速率很高,回收速度无法跟上分配速度,从而导致应用有可能内存溢出。
  状况则是明显的内存泄露问题,因为GC开销较大,但实际回收后释放的空间较小,代表内存中有大量对象无法回收,这可能是由于内存泄漏导致的。同时,也正因为GC次数比较频繁,所以导致应用中的用户线程暂停了工作,停止了对象分配,因而出现了分配速率低的假象。
  对于、状况则需要优化代码,前者需要降低分配速率,后者则需要解决内存泄漏。2。1。4、新生代空间调优思想
  新生代空间的调优核心思想就是需要降低分配速率,简单来说就是少创建对象、多分配空间,以减少GC次数,加大系统吞吐量。但需要值得理解的是:为新生代分配更大的堆空间,反而会使分配速率提高,但新生代空间大了,触发GC的阈值自然会增加,从而能够达到减少GC频率的目的。2。2、年老代提升速率(PromotionRate)
  前面分析的分配速率仅会对新生代空间造成影响,而影响年老代空间的则是另外一个指标:提升速率,也就是指定时间内,新生代升入年老代空间的对象总量,通常单位也为MBS。
  在前面谈论分配速率时,可以根据GC日志计算新生代的分配占比,但新生代升入年老代空间的提升速率又该如何计算呢?因为MajorGC一般都是伴随着FullGC一起发生的,所以无法根据MajorGC计算,比较FullGC时会回收整堆空间。2。2。1、提升速率如何计算?
  同样计算提升速率时,依旧是通过MinorGC日志来计算:1。514:〔GC(AllocationFailure)〔PSYoungGen:38395K5120K(71680K)〕45665K35687K(159232K),0。0570688secs〕〔Times:user0。09sys0。00,real0。06secs〕3。018:〔GC(AllocationFailure)〔PSYoungGen:70326K5104K(71680K)〕100894K105240K(172032K),0。0866792secs〕〔Times:user0。30sys0。02,real0。09secs〕复制代码
  提升速率计算公式:((新生代回收前使用总量新生代回收后使用总量)(整堆回收前使用总量整堆回收后使用总量))(本轮GC时间上轮GC时间)
  GC轮数
  时间差值
  新生代减少
  整堆减少
  提升量
  提升速率
  第一轮
  763ms
  33275KB
  9978KB
  23297KB
  30MBS
  第二轮
  1504ms
  65222KB
  3700KB
  61522KB
  40MBS
  每轮均速
  NULL
  NULL
  NULL
  NULL
  35MBS
  结果如上表,此刻是通过MinorGC日志来计算的提升速率,拆解前面的计算公式可以分析出整体的计算逻辑:先通过新生代回收前后的已使用容量大小,计算出新生代中减少容量。再通过整堆回收前后的已使用容量大小,计算出整个堆空间的减少容量。再通过新生代减少整堆减少,这样可以大致算出新生代中提升到年老代的提升量。该方式只能计算出大概的提升量,因为整堆减少会包含年老代、元空间等区域回收。在通过本次GC触发时间上次GC触发时间,得到本轮GC中程序正常执行的时长。最后通过提示量除执行时长,即可得到JVM的大概提升速率。
  不过在计算提升速率的时候,有个点需要额外注意:Java应用启动后的第一条GC日志不能参与计算,因为第一条GC日志是程序启动后,初次触发GC时输出的,此时堆空间刚从冷状态启动,因此测算出的速率并非程序正常执行时的提升速率。2。2。2、提升速率对JVM的影响
  和分配速率相同,提升速率也一样会影响GC,但它影响的是年老代空间,速率越快也就代表着提升的对象越多,年老代空间被填满的时间会更短,MajorGC被触发的频率也会越快。不过通常情况下,年老代的GC一般会伴随着FullGC一起发生,因此,提升速率越高会最终导致FullGC频率越快。2。2。3、进入年老代的三种异常情况代码存在内存泄漏
  当代码中存在内存泄漏时,会造成堆内存被一点点蚕食,最终导致新生代空间没有空闲内存分配新对象,从而触发JVM的空间分代担保机制,开启对象动态晋升阈值判定,将大量原本未达晋升标准的对象提前迁入年老代空间,以确保新生代拥有足够的空闲内存维护Java应用的正常执行。
  常发性内存泄漏、偶发性内存泄漏、一次性内存泄漏、隐式内存泄漏,不同性质的内存泄漏造成的提升速率增长也不同,后两者引发的速率增长并不大,但前两者,尤其是常发性内存泄漏会带来很大的隐患,最终必然会引发OOM。频繁的大对象分配
  在分代堆中有这么一条法则:超过指定阈值的大对象会被直接送往年老代空间,这条结论是依据对象特性而制定的,正常情况下,大对象都不会是朝生夕死的对象,一般都能够活到成功晋升。因此,为了节省大对象在两个Survivor区中反复挪动带来的开销,JVM会将超过阈值标准的大对象直接分配到年老代。
  大对象直接进入年老代是合理的,但频繁的大对象分配是不合理的,会导致年老代被快速填满,因而频繁触发FullGC。
  大对象直接进入年老代空间,因此大对象分配是不参与前述的提升速率计算公式的。高并发大流量压力
  当系统业务暴涨时,巨大的流量和并发冲击会导致业务线程创建更多的新对象,因而会导致新生代的GC阈值被频繁触发,加快了新生代整体的晋升速度,从而导致提升速率暴涨。
  对于这类正常业务增长导致的提升速率变高,这是系统中的常事,这种情况下只需依照具体业务流量的增长,合理的调大堆空间即可。
  其实归根结底,上述三点都是在围绕着对象被过早提升到年老代这一核心思想展开。对于年老代而言,新生代空间中的所有对象,按部就班的活到15岁再晋升是最佳的状态,因为能够在新生代熬过十多轮GC的对象晋升后,绝大多数情况下会再存活很长一段时间。
  但如果是由于上述三种状况导致对象过早提升到年老代空间,则会带来很大的不稳定因素,有可能很多提早晋升的对象刚晋升,没熬过几轮GC就死了,从而违背了年老代存放长命对象的设计初衷。同时,过早提升还会造成年老代会被快速填满,从而频繁触发FullGC,最终导致Java应用暂停时间过长,影响系统整体的吞吐量。2。2。4、年老代空间调优思想
  年老代空间调优的核心就一点:避免或尽量减少过早提升,为何不是降低提升速率呢?因为在业务规模比较大的情况下,提升速率比较高也是合理的。所以在调优年老代时,只需要将过早提升的对象依旧控制在新生代即可。过早提升的表现一次FullGC后,年老代的空间占用比极速下降。短时间内频繁触发FullGC。提升速率接近分配速率。新生代GC发生后,新生代的空间占用比下降到20以内。过早提升如何解决?
  处理过早提升时,需要根据具体的情况来决定采取何种措施:如果是业务或流量压力变大导致的,那么增大新生代空间即可。如果是代码中存在问题,如内存泄漏或循环体中创建对象等,优化代码即可。如果是短命的大对象分配,如大数组,则可以考虑优化数据结构,如换成链表。2。3、合理的堆空间该如何分配
  Java内存各分区的大小对JVM的性能影响很大,不恰当的空间大小可能会埋下很多故障隐患,同时也会直接或间接影响JVM的提升速率、分配速率,所以如何将各分区调整到合适的大小就成了一个棘手的问题。大部分不具备线上JVM调优实操经验的开发者都会茫然,通常会认为设定的越大越好,但答案却并非如此。
  在指定各区域大小时,可以依据活跃数据大小来进行设定,活跃数据是指应用程序稳定运行后长期存活在堆中的对象,也就是FullGC后年老代中的对象。一般在计算活跃数据大小,都会多次采集程序稳定执行后的FullGC日志,通过取平均值的方式计算出堆中长期存活的年老代总量大小。
  计算出活跃数据大小后,就可以根据其具体值计算出其他分区恰当的值,比例如下:堆空间:活跃数据大小的45倍新生代:活跃数据大小的1。52倍年老代:活跃数据大小的2。53倍元空间:活跃数据大小的1。21。8倍
  假设此时观测出的活跃数据大小为800MB,那堆空间的各区域的大小:堆空间:3200MB新生代:1200MB年老代:2000MB
  当然,这仅作为初始值参考,具体情况取决于应用业务的特性和需求。
  但需注意的是:实际过程中,Xmx、Xms两个参数设定的值必须一致,这样做的好处在于可以避免动态伸缩时带来的性能损耗与空间震荡,因为当JVM内存不足向OS申请内存时都会触发一次全局GC。2。4、GC调优实操思路
  前面几点所提及的都是GC调优的一些方法论以及衡量指标,但在真正需要处理GC调优时,上面几点只能给你提供辅导,并不能建立完善的调优思路,因此,接下来再一同论述GC调优的具体实操思想。
  GC调优时,一般会根据Java程序所装配的垃圾收集器以及具体的GC日志来作为基础进行操作,但不同的垃圾回收器执行的GC日志都是不同的,因此并没有万能的调优策略可以满足所有的性能指标,GC优化要建立在具体的业务场景及环境中,才能达到事半功倍的效果。不过通常GC调优核心步骤如下:明确优化目标实施优化操作跟踪优化结果
  调优前首先需要确定的就是优化目标,到底是需要减少GC停顿,还是增大程序吞吐等,然后再根据目标排除GC日志,分析后根据日志中的分配速率、提升速率、GC频率、GC各阶段停顿时间等指标,实行具体的优化操作。
  同时,也不必奢求一次优化到位,GC调优通常是需要多次进行的,一次优化往往无法达到目标预期,需要不断的根据优化后的GC日志再次制定优化策略,从而最终达到优化目标。
  但GC调优的根本其实是在调对象,如果程序本身代码就存在问题,好比代码中存在频繁创建对象的逻辑,就算你调出花来也无济于事,必须还得从根源上解决问题,这种情况下应当采用jmap工具分析堆使用情况,查看对象分布,从而反向定位代码中的问题并加以解决。2。5、GC优化总结
  凡是涉及性能调优的内容,几乎都必须建立在监控系统之上,不一定要全面,但至少能让调优前有指标数据可参考。对于监控系统中,JVMGC这块建议统计的信息:流量方面:流量峰值、流量均值、用活时间段等。对象方面:分配速率、每个请求的分配均值峰值、提升速率、每次提升总量均值等。GC方面:MinorGC、FullGC停顿时长、GC触发间隔、GC回收总量等。。。。。。。。。。。
  GC调优时的收益排序:改善代码装配合适的GC回收器重新设置内存比例大小调整JVM参数。
  但需重点注意的是:上述的GC调优理论都是基于G1之前的分代垃圾收集器而言的,G1之后的不分代收集器,如:ZGC、ShenandoahGC等压根没必要刻意优化,自身的机制本就足够优异,而且后续的不分代收集器对外暴露的可操作参数也并不多。三、阿里在线排除工具Arthas
  Arthas(阿尔萨斯)是阿里开源的一款Java在线诊断工具,官网原话:当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:这个类从哪个jar包加载的?为什么会报各种类相关的Exception?我改的代码为什么没有执行到?难道是我没commit?分支搞错了?遇到问题无法在线上debug,难道只能通过加日志再重新发布吗?线上遇到某个用户的数据处理有问题,但线上同样无法debug,线下无法重现!是否有一个全局视角来查看系统的运行状况?有什么办法可以监控到JVM的实时运行状态?怎么快速定位应用的热点,生成火焰图?怎样直接从JVM内查找某个类的实例?
  Arthas支持JDK6,支持LinuxMacWinodws,采用命令行交互模式,同时提供丰富的Tab自动补全功能,进一步方便进行问题的定位和诊断。3。1、Arthas快速上手
  对于Arthas工具如果不会使用,其实阿里提供的在线的Terminal学习方式(传送门),可以帮助大家快速上手,下面在本篇中也快速概述一下。
  依照官方的案例演示,先下载并启动提供好的Java案例:wgethttps:arthas。aliyun。commathgame。jarjavajarmathgame。jar复制代码
  再启动一个新的Terminal窗口,下载并启动Arthas工具:wgethttps:arthas。aliyun。comarthasboot。jarjavajararthasboot。jar复制代码
  紧接着Arthas会将本机中所有的Java进程查询出来,类似于jpsps的作用:〔INFO〕arthasbootversion:3。5。5〔INFO〕Foundexistingjavaprocess。。。。。。。〔1〕:161mathgame。jar复制代码
  如果你的机器中启动了多个Java应用,此时会查询出来一个应用列表,我们可以根据前面的序号选择自己要操作的Java应用,如上情况中,再输入1即可:1复制代码
  最终,Arthas成功启动,接下来再通过Arthas提供的指令进行操作即可:
  3。2、Arthas命令详解
  Arthas从最初的发布开始,随着后续社区的活跃性增强及用户群体的不断壮大,指令也越发完善与丰富,至目前为止提供了基础命令、JVM命令、class命令以及字节码增强命令等几大类。3。2。1、基础命令help:查看Arthas命令帮助信息。cls:清空当前屏幕中的所有信息,类似于clear命令。session:查看当前会话的信息。reset:重置所有增强类,还原Arthas增强过的所有类(stop时生效)。version:显示当前的Arthas版本信息。history:输出历史执行过的所有命令。quit:退出当前的Arthas会话,其他会话不受影响。shutdown:关闭所有Arthas会话后,退出Arthas。stop:强制关闭Arthas并中断所有会话。keymap:输出Arthas中所有默认的以及自定义的快捷键。options:查看或设置Arthas的全局开关。pwd:返回当前的工作目录位置,同Linux的pwd命令。3。2。2、类命令sc:查看JVM已加载的类信息,可选项如下:classpattern:类名表达式匹配(必填),如scjava。lang。String。E:开启正则表达式匹配,默认为通配符匹配。c:指定class的类加载器的哈希码。d:显示当前类的详细信息,包含来源、声明、类加载相关等信息。f:输出当前类的属性成员信息,与d一同使用。x:指定输出静态变量时属性的遍历深度,默认为0。n:具有详细信息的匹配类的最大数量(默认为100)。sm:查看已加载类的方法信息,可选项如下:classpattern:类名表达式匹配(必填),如smjava。lang。String。E:开启正则表达式匹配,默认为通配符匹配。d:查看方法的详细信息,配合方法名使用,如smdjava。lang。StringtoString。c:同scc作用相同。n:同sch作用相同。jad:反编译指定已加载类的源码,可选项如下:c、E都与前面的作用相同,举几个案例演示用法。jadsourceonlyjava。lang。String:只显示反编译后的Java源码。jadjava。lang。String:反编译指定类。jadjava。lang。StringtoString:反编译指定类的某个方法。mc:内存编译器,编译。java源文件为。class类文件,可选项如下:c:指定类加载器(以哈希码的方式指定)。d:指定编译后的类文件输出位置。redefine:加载外部的。class文件,重新加载JVM已加载的类。推荐使用retransform代替redefine。retransform:作用与redefine相同,热部署的作用,用于线上替换类方法。注意点:重新替换JVM中被加载的类时,不能新增方法或属性。正在执行的方法不能替换。dump:导出已加载类的字节码数据到指定目录,可选项如下:c、E作用与之前的相同。d:指定输出的路径,如dumpdusrdatabyteCodejava。lang。String。classloader:查类加载器的继承树,urls,类加载信息,可选项如下:a:显示所有类加载器加载的所有类。c:查看指定的类加载器的加载路径,如classloaderc14ae5a5。l:统计每个类加载器的加载信息。r:查找某个的资源路径,配合c使用,如classloaderc33909752rjavalangString。class。t:以树结构列出每个类加载器之间的父子关系。u:显示类加载器的url统计信息,如加载总数、父子关系、加载范围等。i:查看每种类加载器的实例数量及其加载总量。3。2。3、JVM命令dashboard:资源监控仪表盘,包含线程、内存、GC、运行环境等信息,可选项如下:i:刷新实时数据的间隔时间,默认为5000ms。n:刷新实时数据的次数,默认为一直持续刷新,按ctrlc退出。thread:查看当前线程的堆栈信息,可选项如下:n:显示最活跃的n条线程信息,如threadn5。i:指定活跃性统计的采样间隔时间,如threadi5000。b:自动检测出应用中当前阻塞其他线程的线程。state:查询目前程序中处于指定状态的线程,如threadstateBLOCKED。id:查看某个线程的详细信息,如thread21。jvm:查看JVM信息,包含线程内存OS内存结构编译类加载运行环境等信息。sysprop:查看或修改当前JVM的系统属性,如syspropjava。home。sysenv:,查看当前JVM的环境参数。vmoption:查看或修改JVM的运行时参数,如:vmoptionPrintGC:查看PrintGC是否开启。vmoptionPrintGCtrue:更改PrintGC参数。logger:查看logger信息,更新loggerlevel。getstatic:查看类的静态属性,用法:getstaticclassnmaefieldname。ognl:执行ognl表达式,使用方式可参考:官方指南、特殊用法。heapdump:类似于jmap工具的堆dump功能,使用方式:heapdumpusrdatadumpheap。hprof:导出堆快照到指定文件。heapdumpliveusrdatadumpheap。hprof:只导出存活对象的快照。mbean:查看Mbean的信息,详情参考:官方文档。memory:查看JVM的内存划分、内存结构以及占用率。3。2。4、字节码增强命令tt:记录指定方法每次执行的数据,并能在不同的时间下调用观测,可选项如下::指定要观测的类名方法名。t:记录下方法每次执行的情况,如tttdemo。MathGameprimeFactors。i:查看某条执行记录的执行详情,如tti1000。d:删除某条执行记录,配合i使用,ttdi1000。n:设置执行次数,如tttn10demo。MathGameprimeFactors。l:显示目前已存在的所有执行记录。p:重新执行某条执行记录,配合i使用,如tti1001p。s:通过OGNL表达式进行查找。M:指定接收结果的字节上限,默认为1KB。replaytimes:配合p使用,指定重新执行N次。replayinterval:执行多次时,每次执行时的间隔时间。重新执行3次某记录,每次间隔500ms:tti1001preplaytimes3replayinterval500。liwatch:观测指定方法的执行情况,可选项如下:b:在方法调用之前观测。s:在方法成功执行后观测。e:在方法异常执行后观测。f:在方法结束后进行观测(默认)。n:指定观测的次数。使用示例:watchsn10demo。MathGameprimeFactorsmonitor:对指定的方法执行进行监控,可选项如下:c:指定监控的周期,默认为60s。n:指定监控的周期次数。使用示例:monitorc10n3demo。MathGameprimeFactorsstack:输出当前方法被调用的调用路径。trace:方法内部调用路径,并输出方法路径上的每个节点上耗时,可选项如下:i:跳过JVM的本地方法。n:和之前的n同义。ul3。2。5、Arthas的OGNL表达式
  Arthas中的很多进阶操作都需要依赖于OGNL表达式进行编写,因此想要玩转Arthas,自然需要对于OGNL也具备一定的基本功,接下来演示一些常规操作,详细的使用方式可参考:官方指南、特殊用法。、调用静态属性
  ognl类的全限定名静态属性名
  示例:〔arthas80573〕ognldemo。MathGamerandom复制代码、调用静态方法
  ognl类的全限定名静态方法名(参数)
  示例1:调用入参为基本数据类型和集合的方法:〔arthas80573〕ognldemo。MathGameprint(100,{1,2,3,4})x1null复制代码
  示例2:调用入参为对象类型的方法:〔arthas80573〕ognlobjnewjava。lang。Object(),xxx。xxxxxx(obj)x1复制代码
  示例3:调用入参为Map类型的方法:〔arthas80573〕ognlmap{k1:v1,k2:v2},xxx。xxxxxx(map)x1复制代码
  示例4:将一个方法的执行结果作为另一个方法的入参:〔arthas80573〕ognlresultxx。xxA(),xx。xxxx(result)x1复制代码、调用构造方法
  ognlnew类的全限定名()
  示例1:调用无参创建对象〔arthas80573〕ognlnewjava。lang。Object()复制代码
  示例2:调用有参创建对象〔arthas80573〕ognlnewxxx。xx。xxx(xx,x,{1,2,3})复制代码
  示例3:调用存在对象引用类型的构造函数创建对象〔arthas80573〕ognlobjnewnewjava。lang。Object(),newxxx。xx。xxx(obj)复制代码、读取不同类型的值
  示例1:读取引用对象类型的属性值〔arthas80573〕ognl类全限定名方法名(参数)。属性名称复制代码
  示例2:读取List类型的指定元素〔arthas80573〕ognl类全限定名方法名(参数)〔下标〕复制代码
  示例3:读取Map类型的指定元素〔arthas80573〕ognl类全限定名方法名(参数)〔key〕复制代码。。。。。。。。。
  详细的OGNL语法可参考:官方指南,在线上排查时往往会结合tt、watch、monitor、stack、trace等多个命令共同使用。3。3、Arthas线上常用场景
  Arthas中集成了大部分JDK工具的功能实现,因此,在线上情况时,可以通过它快速的帮助我们解决问题,如CPU占用过高、线程阻塞、死锁、代码动态修改、方法执行缓慢、排查404等。3。3。1、排查CPU占用过高问题使用threadn10命令查看CPU占用资源最高的10条线程。使用thread命令查看前几条线程的详细执行信息,定位到具体的方法。使用monitor命令对前面定位到的方法进行监控,查看方法的调用次数与耗时。分析monitor命令查询出的结果,定位问题根源,确定是由于调用过于频繁导致的,还是内部代码逻辑问题。使用jad命令反编译class文件,根据前面分析的原因排查代码并改善。3。3。2、排查线程阻塞问题使用thread查看所有线程信息,再筛选所有阻塞状态的线程。根据线程名称定位具体的业务模块,再选中该业务中的一条线程查看堆栈信息。根据线程堆栈信息定位导致阻塞的具体方法,再利用stack命令查看方法堆栈信息。利用jad工具反编译源码,分析业务逻辑代码并改善。3。3。3、排查死锁问题利用Arthas来检测死锁特别简单,只需要执行一行命令threadb即可。3。3。4、排查方法执行过慢问题通过trace命令排查方法执行速度,tracexx类xx方法cost50ms,观测执行时间大于50ms的该方法的调用信息。可以结合正则表达式,同时排查多个类、多个方法,traceEClassAClassBmethod1method2method3。3。3。5、动态修改线上代码
  有些项目编译可能需要两小时,好容易编译完成上线之后,发现代码有一处小地方存在逻辑错误需要更改,此时难度需要重新将其下线,重新更改后打包部署吗?有了Arthas之后的你完全不需要这样干。通过jad将要修改的类反编译为。java文件,输出到指定目录。本地纠正。java文件后,通过mc命令重新编译。java文件。通过redefine或retransform命令将刚编译的。class文件再次加载到JVM中。
  这个功能是Arthas非常实用的一个功能,往往在线上环境被用于代码纠错、日志级别修改、Java配置文件修改等场景。3。3。6、。。。。。。。。。。。
  显然,Arthas还有更多的应用场景等待你去探索,根据不同的业务场景以及遇到的不同问题,利用Arthas都可以实现很好的排查与解决,上述中仅列出一些常见的应用场景。四、不同场景下的最佳配置推荐
  线上JVM的最佳参数配置往往要根据实际的业务场景以及运行环境进行思量,首先需要弄明白业务是追求响应速度还是吞吐量,再者需要结合所部署的硬件配置及服务器环境综合考虑,下面提供一些配置参数给予大家用作参考。4。1、运行时数据区4。1。1、堆空间
  之前曾提及到,运行时数据区最佳的空间大小,以活跃数据大小进行作为基础参考,然后进行设置:
  无论你的项目是追求响应速度,亦或是吞吐量,都可根据活跃数据计算的大小作为基础进行调整,依照活跃数据计算出的大小也恰巧能够符合Sun公司官方给出的推荐,如:
  新生代空间的最佳占比应当在堆总大小的38,换算成百分比为37。5。
  通过上图中根据活跃数据获取的各分区大小进行计算:
  1200MB(Eden)3200MB(Heap)0。375(37。5),和官方的推荐完全一致。
  那么实际项目上线时,活跃数据大小如何获取呢?可以在测试阶段进行压测,然后通过GC日志进行计算。不过基于活跃数据计算出的大小也可以根据业务进行调整。对象存活较高的业务,Survivor区与Eden区比值建议为2:4,即XX:SurvivorRatio4。对象晋升年龄阈值建议:对象存活率较低的业务:保留默认值,即15。对象存活率较高的业务:建议调小,如XX:MaxTenuringThreshold7,可以减少大量存活对象在幸存区反复横跳带来的性能开销。JIT编译的热点代码缓存区至少64M,即XX:ReservedCodeCacheSize64m。TLAB线程私有区域可以调整为Eden区的110,即XX:TLABWasteTargetPercent10。记得打开OOM时Dump堆的参数,以及执行脚本可以指定为重启应用。
  1。8及以上版本的JDK大多数情况下,只需要调整好每个分区的大小即可,其他的优化参数,大多数JVM都会默认开启。
  Xms、Xmx两参数的值需保持一致,防止由于内存动态伸缩时造成抖动影响性能。4。2、元空间
  元空间的大小建议:一般在活跃数据的1。2倍左右足够,如果程序内使用大量动态代理,可以尝试加大到1。5、1。8倍。4。3、栈空间
  HotSpot中,Java虚拟机栈和本地方法栈合二为一了,因此这里的栈空间涵盖了这两个概念。
  JDK1。5之前默认栈大小为256K,1。5之后默认为1M大小,对于该值的调整要基于业务来决定,如果业务执行时,方法调用链不会太长,可以适当缩小到512k,即Xss512K,这样做的好处在于:在物理内存相同的情况下,该值越小,程序中就能产生更多的线程,从而能够拥有更多的线程处理客户端到来的请求。
  但操作系统不可能允许一个进程无限制的创建线程,因此单个进程中的线程数量一般最多控制30005000最佳。4。2、GC垃圾收集
  GC方面也是JVM调优中操作性最大的部分,因此,这部分在JVM调优额外重要。4。2。1、选择垃圾收集器
  选用合适的垃圾收集器往往能够让你的应用性能提升一大截,但合适的收集器也需要根据运行环境及业务场景去选择,那如何选择最合适的收集器呢?、将堆空间调整到合适的大小后,优先让JVM自行根据配置选择。、如果内存小于100MB或部署在单核双核机器,使用串行收集器。、JDK8及以前追求低延迟(响应速度)选ParNewCMS,追求高吞吐则选PSPO。、后续新版本的JDK中,8GB以上可以考虑选用G1,上百GB规模可采用ZGC。4。2。2、ParNewCMS组合参数推荐使用ParNewCMS组合:XX:UseParNewGCXX:UseConcMarkSweepGC。并行收集GC线程数建议为CPU核数,即XX:ParallelCMSThreadsCPUcore。内存碎片整理方面(MSC工作):XX:UseCMSCompactAtFullCollection:内存碎片化严重时开启MSC整理。建议将每次FullGC后的内存整理改为23轮触发一次,即XX:CMSFullGCsBeforeCompaction。因为是追求响应速度的组合,因此目标停顿时间可以适当偏小一些,即XX:MaxGCPauseMillis。激进优化策略:XX:CMSParallellnitialMarkEnabled:在初始阶段采用多线程执行。XX:CMSParallelRemarkEnabled:在重新标记阶段采用多线程执行。XX:CMSScavengeBeforeRemark:在重新标记阶段前触发一次新生代GC。4。2。3、ParallelScavengeParallelOld组合参数推荐使用PSPO组合:XX:UseParallelGCXX:UseParallelOldGC。并行收集GC线程数建议为CPU核数,即XX:ParallelGCThreadsCPUcore。因为是追求吞吐的组合,因此吞吐比尽量可以调高,即XX:GCTimeRatio,如若无经验没法预估准确值,那则可以开启JVM的自适应调整策略:XX:UseAdaptiveSizePolicy。4。2。4、G1整堆收集器参数推荐使用G1收集器:XX:UseG1GC。不要强制使用Xmn参数设置年轻代的大小,因为G1是通过动态调整年轻代大小达到目标暂停时间的目的。如果分配的对象平均体积过大,可以适当调大每个分区的Size,但必须要为2的次幂,即通过XX:G1HeapRegionSize调整,正常情况下尽量不要手动调整。尽量可以将并发线程数调整的大一些,即XX:ConcGCThreads,一般推荐为CPU核数12。手动指定触发混合GC的阈值,关闭IHOP适应分析,消除自适应计算的耗时,XX:InitiatingHeapOccupancyPercent45XX:G1UseAdaptiveIHOP。混合GC时间过长可微调该三个参数:XX:G1MixedGCCountTarget8XX:G1MixedGCLiveThresholdPercent88XX:G1HeapWastePercent5。4。3、性能激进优化策略
  在JDK1。7及其之后的版本中,JVM推出了很多激进优化的策略,但在1。8及其之后的环境中,大部分的参数都是默认开启的,因此我们没有必要显式再次开启。但其实JVM中的一些激进优化参数默认也并未打开,如果你的程序堆空间足够大,也可以尝试开启后优化程序性能。XX:ParGCCardsPerStrideChunk4096:CMS激进优化策略,增大GC线程扫描卡表的范围,默认为256,三个最佳值为32768、4K、8K。XX:AlwaysPreTouch:开启物理内存分配替换虚拟内存分配,优化分配率。XX:UseLargePages:启用内存大页面分配技术。XX:UseBiasedLocking:关闭偏向锁,在并发较高的系统中关闭反而可以提升性能。XX:AutoBoxCacheMax20000:加大IntrgerCache的缓存。XX:UseCounterDecay:关闭JIT即时编译器的热度衰减机制(会消耗一定内存)。XX:TieredCompilation:关闭C1静态编译器编译,直接使用C2编译。XX:MaxDirectMemorySize:直接内存大小如果确认用的比较少,可以调小,如果用的比较多,可以适当调大。4。4、不同的启动方式参数设置方式IdeaEcalipse:在运行时的选项卡中配置,如IDEA的Configurations。。。VMOptions中。Tomcat:bin目录下的catalina。sh文件中的JAVAOPTS的值上写JVM参数即可。jar包方式启动直接将VM参数跟在后面即可。五、总结
  对于性能优化这个内容而言,没有绝对正确或最佳的参数,也包括本章的内容你可以适当参考但不能照搬于生产环境,安全第一,项目能够稳定执行是根本,性能优化永远要建立在应用健康运转但遭遇瓶颈的基础上,不要随便调优,更不要刻意调优。
  同时,对于JDK不同版本中的默认值,如果你不清楚其具体作用,那建议保留默认值,毕竟JDK默认将其设为此值总有它的理由,默认值至少能够满足绝大部分的项目需求。因此,如若你没有丰富的激进优化经验,再次重申:不要随意更改一些性能参数的默认值。

祝贺!27岁国乒名将大婚世青赛夺12金,老公是企业家,嫁入豪经历了东京奥运会,运动员们都在积极备战巴黎奥运会,本身东京奥运会就是延期开幕,留给运动员的时间并不多,为了梦想的名将们都在积极的调整状态。不过,也有一些运动员选择了退役,下一个……奥比岛手游微光考核攻略奥比岛手游微光考核怎么过?微光考核是一个亲密度考核关卡,小编带来奥比岛手游微光考核攻略,希望可以帮到大家。奥比岛手游微光考核攻略1、首先要和好友进同样的图,可以进去截图或……豪华纯电动轿车怎么选?浅析顶配版Polestar3首年用车成买车要考虑自己的经济条件,根据自己的收入水平选择适合自己的车型。对于很多工薪阶层来说,月收入五六千元,在生活成本的支出上就已经捉襟见肘了。所以在考虑买车时,除了新车的售价之外,……各国十大热门旅游景点之瑞士一、万国宫位于日内瓦东北郊的日内瓦湖畔,与巍峨的阿尔卑斯山遥遥相望。它又名国联大厦,是联合国的前身国际联盟的总部所在地,现为联合国日内瓦办事处,又称联合国欧洲总部。……杜锋真敢玩!第三节全板凳迎战北控!全场板凳拿29分杜锋真敢玩!12月24日,CBA常规赛广东东莞大益迎战北控。广东东莞大益在比赛当中发挥出色,早早就领先了北控20多分,在第三节比赛领先30多分的时候,广东东莞大益让主力全……美国Mcmastercarr在数控编码上的应用,mcmast美国Mcmastercarr在数控编码上的应用,mcmastercarr中国代理北富联机电有限公司机器通过数控操作,其中指定软件程序来控制对象。加工背后的语言也称为G代码,用于……202224商品期货复盘铁矿螺纹纯碱ptapvc甲醇燃油声明:本文仅为个人期货复盘,不构成投资建议。交易心得:交易要顺趋势而为,不要逆势交易1。铁矿1h走了个顺平不新高结构,该强不强就是弱。当下走1h2卖一笔下中,黑色系……摩洛哥前主帅哈利霍季奇不能原谅他们夺走属于我的骄傲直播吧12月25日讯摩洛哥在2022年世界杯杀进四强,但前主帅哈利霍季奇至今依然不满自己在世界杯前下课。哈利霍季奇在2019年成为摩洛哥国家队主帅,并率队通过预选赛获得了……未售先火口碑霸榜vivoX90系列改写高端市场新格局11月22日晚,vivoo高端旗舰X系列新十年的开篇之作vivoX90系列重磅亮相,随着全球首发天玑9200、一英尺大底主摄、自研芯片V2、双焦段人像镜头等硬核技术亮相,viv……开放孔道金属粒子超晶格开放孔道金属粒子超晶格文章出处:YuanweiLi,WenjieZhou,IbrahimTanriover,WisnuHadibrata,BenjaminE。Partri……南京旅游攻略在南京一定要做的52件小事1。去南京博物院感受历史文化底蕴2。拍照打卡民国照相馆,租好旗袍拍拍拍3。逛一逛明瓦廊小吃街4。去金陵大报恩寺塔感受科技元素与历史遗迹的融合5。在老门东……CBA三消息广东辟谣试训外援,浙江广厦迎德比,李添荣成为核心大家好呀,我是北柠,各位小伙伴们要养成先赞后看的习惯哦!现在CBA休赛期的时间已经越来越少了,各支球队也在进行最后阶段的冲刺工作,下个赛季的CBA联赛将会在10月10日正……
Springboot多模块项目搭建Springboot多模块项目搭建打包jar运行打包war运行一个启动类(有的项目是多模块多个启动类),为后续的SpringCloud项目作准备。父工程:father子模块:s……到了西双版纳,都是东北老铁冬日秘境西双版纳春节已过大半,每年到这个时候的中国人都离不开两个关键词:返乡,和旅游。不知从何时起,春节旅游这一命题,开始牢牢地与海南二字绑在一起。从入冬开始……原神圣遗物摆烂哪家强?满星深渊种门来大家都知道原神中圣遗物副本坐牢有多痛苦,从零开始角色的圣遗物提升进度在最后都是看脸,非酋坐牢长达以年为单位,欧皇一天毕业。在炼金合成台还圣奥迹出来后,部分坐牢副本的情况得到改善……苹果AppleTV4K用户反馈SiriRemote遥控器出现IT之家1月20日消息,不断有苹果第三代AppleTV4K用户在Reddit社区、MacRumors论坛、苹果官方支持论坛、Twitter等社交媒体上反馈SiriRemote遥……吃鱼爱好者心碎!研究警告吃一条淡水鱼,等于喝了一个月重度污染一项新研究显示,一年只吃一条淡水鱼,就相当于喝了一个月含有永久化学物质的水。来自环境工作组(EWG)的研究人员分析了500份来自美国各地水体的本地捕捞鱼片样本。他们计算出……2250万,回归勇士甘愿牺牲!科尔没有给你足够的尊重,你应该23胜24负,西部第10,说实话,本赛季勇士除非做出一些具有决定性的补强交易,否则的话,在大部分人眼中,他们看起来想要夺冠,非常困难。赛季已经过去了一大半,就勇士目前的这套阵容……23岁患癌后,他被隐瞒病情带去参赛,倒在了奥运大门前2月5日晚,由武大靖、任子威、范可新领衔的中国短道速滑队旗开得胜,收获北京冬奥会混合2000米接力金牌,为中国体育代表团赢得首金。讲真,在短道速滑这个项目上,我国的实力还……90后00后也得肾病了?肾病越来越年轻化,因为现在年轻人对于自己身体的保护意识越来越低,总觉得自己身强体壮,身体很健康,不要进行保养;又由于各种坏习惯不知道改变,导致肾病的发病率在年轻人当中也越来越高……中国出入境航班大幅上量,国际航协呼吁各国停止设置旅行障碍(王潇雨摄影)华夏时报(www。chinatimes。net。cn)记者王潇雨黄兴利北京报道在实施严格的出入境管控政策近三年之后,中国即将在1月8日重新全面恢复与全……去年哪家煤企赚得最多?多家煤炭企业发布2022年业绩预告。近日,多家煤炭企业发布2022年业绩预告,大部分煤炭企业业绩预计大幅增长。其中,中国神华、陕西煤业、兖矿能源、潞安环能、山西焦煤5家上……地球毁灭是?据各种呼吁说人类要毁灭地球,其实人类压根儿就没有毁灭地球的能力,人类唯一能毁灭了就是自己。地球也是有周期性的就算把世界上所有的核武器合在一起,引爆后的能量也就相当于……前有环江西高铁圈,今有环江西自贸区,江西你被谁抛弃?2013年央视一张环江西高铁圈让江西一夜成名,此后环江西现象层出不穷。如今,环江西现象又多一笔,那就是环江西自贸区。在自贸区遍地开花的今天,江西是目前还没有获批建设自贸区……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网