jvm调优基本思路,jvm调优主要是调哪里

  jvm调优基本思路,jvm调优主要是调哪里

  假设您已经知道运行时数据区和常见的垃圾收集算法,以及Hotspot支持的垃圾收集器。

  第一,cpu占用率太高。cpu占用要根据情况来讨论。业务中有活动吗?突然大量流量进来,活动结束后cpu占用率下降。如果是这样的话,就不用太在意了,因为请求越多,需要处理的线程就越多。这是正常现象。

  话又说回来,如果你的服务器配置本身就很差,cpu只有一个核心,这种情况下,稍微多一点流量真的会耗尽你的cpu资源。这时候你应该考虑先升级配置。

  第二种情况,cpu利用率长期过高。在这种情况下,可能是你的程序有超级多循环的代码,甚至是无限循环。故障排除步骤如下:

  (1)使用top命令检查cpu使用情况。

  这样就可以定位cpu高的进程。在linux下,top命令获得的进程号和jps工具获得的vmid是相同的:

  (2)使用top -Hp命令查看线程。

  可以看到线程id是7287,这个线程一直在占用cpu。

  (3)将线程号转换为十六进制[root @ localhost ~]# printf % x 7287

  1c77记下这个十六进制数,我们接下来会用到。

  (4)使用jstack工具[root @ localhost ~]# jstack 7268 grep1c 77-A10检查线程堆栈

  http-nio-8080-exec-2 #16守护进程prio=5 OS _ prio=0 tid=0x 00007 FB 66 ce 81000 NID=0x1c 77 runnable[0x 00007 FB 639 ab 9000]

  状态:可运行

  at com . spare yaya . JVM . service . endlessloopservice . service(endlessloopservice . Java:19)

  at com . spare yaya . JVM . controller . JVM controller . endless loop(JVM controller . Java:30)

  位于sun . reflect . nativemethodaccessorimpl . invoke 0(本机方法)

  位于sun . reflect . nativemethodaccessorimpl . invoke(nativemethodaccessorimpl . Java:62)

  位于sun . reflect . delegatingmethodaccessorimpl . invoke(delegatingmethodaccessorimpl . Java:43)

  位于Java . lang . reflect . method . invoke(method . Java:498)

  位于org . spring framework . web . method . support . invokablehandlermethod . doinvoke(invokablehandlermethod . Java:190)

  位于org . spring framework . web . method . support . invokablehandlermethod . invokeforrequest(invokablehandlermethod . Java:138)

  在org . spring framework . web . servlet . MVC . method . annotation . servletinvocablehandler method . invokeandhandle(servletinvocablehandler method . Java:105)通过jstack工具输出当前线程栈,然后通过grep命令结合上一步得到的线程的十六进制id定位该线程的运行状态,其中jstack后的7268是位于步骤(1)的进程号,grep后的步骤(2)和(3)的线程号。

  从输出结果可以看出,这个线程正在运行。当方法com。spare yaya . JVM . service . endlessloopservice . service被执行,代码行号是19行,所以可以到这19行代码,找到它所在的代码块,看它是否在循环中,从而定位问题。

  2.死锁死锁不像第一种情况那样明显。web应用程序绝对是一个多线程程序,它服务于多个请求。程序死锁后,死锁线程处于等待状态(WAITING或TIMED_WAITING)。等待线程不占用cpu,其内存消耗也非常有限。另一方面,可能是请求无法提出,最后超时了。死锁少的时候,不容易发现。

  可以用jstack工具查看。

  (1)jps查看java进程[root@localhost ~]# jps -l

  8737 sun.tools.jps

  882 JVM-0 . 0 . 1-snapshot . jar(2)JStack视图死锁问题由于web应用往往有很多工作线程,特别是在高并发的情况下,线程的数量更多,所以这个命令的输出内容会非常多。jstack最大的好处就是会把死锁信息(包括哪个线程生成的)输出到最后,所以我们只需要看最后的内容。

  上面列出的线程的Java堆栈信息:

  ===================================================

  “线程-4”:

  at com . spare yaya . JVM . service . deadlockservice . service 2(deadlockservice . Java:35)

  -等待锁定0x 000000000 f 5035 AE 0(Java。郎。对象)

  -锁定0x 00000000 f 5035 af 0(Java。郎。对象)

  在com。饶了丫丫吧。JVM。控制器。JVM控制器。lambda$deadLock$1(JVM控制器。Java:41)

  在com。饶了丫丫吧。JVM。控制器。JVM控制器$ Lambda $ 457/1776922136。运行(未知来源)

  位于Java。郎。线程。运行(线程。Java:748)

  线程3 :

  在com。饶了丫丫吧。JVM。服务。deadlockservice。服务1(deadlockservice。Java:27)

  -等待锁定0x 000000000 f 5035 af 0(Java。郎。对象)

  -锁定0x 00000000 f 5035 AE 0(Java。郎。对象)

  在com。饶了丫丫吧。JVM。控制器。JVM控制器。lambda$deadLock$0(JVM控制器。Java:37)

  在com。饶了丫丫吧。JVM。控制器。JVM控制器$$ 456/474286897。运行(未知来源)

  位于Java。郎。线程。运行(线程。Java:748)

  发现一个死锁。发现了一个死锁,原因也一目了然。

  三、内存泄漏我们都知道,java和c的最大区别是前者会自动收回不再使用的内存,后者需要程序员手动释放。在c中,如果我们忘记释放内存就会发生内存泄漏。但是,不要以为虚拟机(Java虚拟机的缩写)帮我们回收了内存就不会出现内存泄漏。

  程序发生内存泄漏后,进程的可用内存会慢慢变少,最后的结果就是抛出伯父错误。发生伯父错误后可能会想到是内存不够大,于是把-Xmx参数调大,然后重启应用。这么做的结果就是,过了一段时间后,OOM依然会出现。最后无法再调大最大堆内存了,结果就是只能每隔一段时间重启一下应用。

  内存泄漏的另一个可能的表现是请求的响应时间变长了。这是因为频繁发生的乔治勋章会暂停其它所有线程(停止世界)造成的。

  为了模拟这个场景,使用了以下的程序

  导入Java。util。并发。执行服务;

  导入Java。util。并发。遗嘱执行人;

  公共类主要{

  公共静态void main(String[] args) {

  Main Main=new Main();

  while (true) {

  尝试{

  线程。睡眠(1);

  } catch (InterruptedException e) {

  e。printstacktrace();

  }

  主要的。run();

  }

  }

  私有空运行(){

  执行者服务=执行者。newcachedthreadpool();

  for(int I=0;i i ) {

  executorService.execute(() - {

  //做点什么.

  });

  }

  }

  }运行参数是-Xms20m -Xmx20m -XX: PrintGC,把可用内存调小一点,并且在发生千兆周时输出信息,运行结果如下

  [GC(分配失败)12776K- 10840K(18432K),0.0309510秒]

  [GC(分配失败)13400K- 11520K(18432K),0.0333385秒]

  [GC(分配失败)14080K- 12168K(18432K),0.0332409秒]

  [GC(分配失败)14728K- 12832K(18432K),0.0370435秒]

  [全气相色谱(人体工程学)12832K- 12363K(18432K),0.1942141秒]

  [全气相色谱(人体工程学)14923K- 12951K(18432K),0.1607221秒]

  [全气相色谱(人体工程学)15511K- 13542K(18432K),0.1956311秒]

  .

  [全气相色谱(人体工程学)16382K- 16381K(18432K),0.1734902秒]

  [全气相色谱(人体工程学)16383K- 16383K(18432K),0.1922607秒]

  [全气相色谱(人体工程学)16383K- 16383K(18432K),0.1824278秒]

  [完全GC(分配失败)16383K- 16383K(18432K),0.1710382秒]

  [全气相色谱(人体工程学)16383K- 16382K(18432K),0.1829138秒]

  [螺纹主16383k-16382k(18432k)中的完全GC(人体工程学)异常,0.1406222秒]

  [完整GC(分配失败)16382K- 16382K(18432K),0.1392928秒]

  [全气相色谱(人体工程学)16383K- 16382K(18432K),0.1546243秒]

  [全气相色谱(人体工程学)16383K- 16382K(18432K),0.1755271秒]

  [全气相色谱(人体工程学)16383K- 16382K(18432K),0.1699080秒]

  [完整GC(分配失败)16382K- 16382K(18432K),0.1697982秒]

  [全气相色谱(人体工程学)16383K- 16382K(18432K),0.1851136秒]

  [完整GC(分配失败)16382K- 16382K(18432K),0.1655088秒]

  Java。郎。内存不足错误:Java堆空间可以看到虽然一直在gc,占用的内存却越来越多,说明程序有的对象无法被回收。但是上面的程序对象都是定义在方法内的,属于局部变量,局部变量在方法运行结果后,所引用的对象在千兆周时应该被回收啊,但是这里明显没有。

  为了找出哪些对象回收失败,我们添加了运行参数-xx: heapdumppath=heap.bin,意思是发生OOM时转储堆内存信息。运行程序直到异常,然后获取heap.dump文件。然后我们借助eclipse的MAT插件进行分析。如果没有安装,需要先安装。

  然后文件-打开堆转储.然后选择刚刚转储的文件,并选择泄漏嫌疑。

  MAT将列出所有可能泄漏内存的对象。

  可以看到实际上有21,260个线程对象和3,386个ThreadPoolExecutor对象。如果看Java . util . concurrent . thread pool Executor的源代码,可以发现线程池为了重用线程,会不断等待新的任务,线程不会被回收。需要调用它的shutdown方法,让线程池在任务完成后停止。

  实际上,线程池被定义为一个局部变量,将它设置为singleton是一个很好的做法。

  以上只是处理方法之一。

  对于在线应用来说,内存往往设置得很大,这样发生OOM然后转储内存快照的文件会很大,可能会大到无法在本地计算机上分析(因为内存不够打开这个转储文件)。这是另一种治疗方法:

  (1)使用jps定位进程号c:\ users \ spare yaya \ idea projects \ maven-project \ target \ classes \ org \ example \ net jps-l

  24836 org.example.net.Main

  62520 org . jetbrains . jps . cmdline . launcher

  129980 sun.tools.jps.Jps

  16028 org。Jetbrains.jps.cmdline.launcher因为你已经知道哪个应用有OOM,所以可以直接用jps找到进程号135988。

  (2)用jstat分析gc活动jstat是一个统计java进程内存使用量和gc活动的工具。参数很多,可以通过jstat -help查看所有参数及其含义。

  c:\ Users \ spare yaya \ idea projects \ maven-project \ target \ classes \ org \ example \ net jstat-gcutil-t-h8 24836 1000

  时间戳S0 S1东部时间上午CCS YGC YGCT FGC FGCT GCT

  29.1 32.81 0.00 23.48 85.92 92.84 84.13 14 0.339 0 0.000 0.339

  30.1 32.81 0.00 78.12 85.92 92.84 84.13 14 0.339 0 0.000 0.339

  31.1 0.00 0.00 22.70 91.74 92.72 83.71 15 0.389 1 0.233 0.622以上是命令,表示gc的输出,输出时间,每8行一个头信息,统计的进程数是24836,每1000毫秒输出一次信息。

  输出的信息是时间戳是从jvm启动开始的时间,S0、S1和E是新一代的两个幸存者和Eden,O是旧的年龄区,M是Metaspace,CCS使用压缩比,Ygc和YgcT是新一代gc的次数和次数,Fgc和FgcT是老一代GC的次数和次数,GCT是GC的总时间。虽然发生了gc,但是陈年的内存占用率一点都没有降低,说明有些对象是不能回收的(虽然不排除这些对象真的有用)。

  (3)用jmap工具转储内存快照jmap可以转储指定java进程的内存快照。效果和第一种方法一样,只是不用等OOM就可以了,转储的快照会小很多。

  Jmap-dump: live,format=b,file=heap.bin24836然后你会得到heap.bin的内存快照文件,然后你就可以用eclipse来分析了。

  第四,总结以上三种方法。严格来说,它们不是jvm调优,而是用jvm工具找出代码中的问题。我们的jvm的主要目的是最小化暂停时间并提高系统的吞吐量。

  但如果不经过系统分析就盲目设置参数,可能会得到更差的结果。随着jvm的发展,各种默认参数可能会被实验室里的人经过多次测试来平衡,适用于大多数应用场景。

  如果您认为真的有必要调优您的jvm,您还必须对其进行采样和分析。最后你还得慢慢调整几次才能得到更好的效果。

  版权归作者所有:原创作品来自博主小二上九8,转载请联系作者取得转载授权,否则将追究法律责任。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: