JVM问题总结

1、如果cpu使用率一直过高如何处理

      a)  top命令查询到占用率比较高的进程pid, 如果是java进程,接下来进一步分析

      b)接下来根据进程pid,去查询相应的线程id信息

           ps -mp pid -o THREAD,tid,time

       c)找到使用率比较高的线程id,假设为6677,通过linux命令转为16进制

            printf  "%x\n" 6677 结果是1a6e

       d)通过jstack查询线程堆栈信息

            jstack pid | grep 1a6e

       e)如果上一步得到的是gc线程

           jstat -gcutil  pid 2000 10 每两千毫秒打印一次java虚拟机当中堆内存信息

       f)如果上一步当中,我们发现频繁full gc,接下来

          jmap -dump:format=b,fild=dump.bin pid 将堆栈信息dump出来,在本地通过jvisualvm进行分析。

2、线上频繁发生full gc如何进行排查

      首先,需要弄清楚什么情况下会发生full gc

      ① 程序当中调用了system.gc

      ② 执行了jmap -histo:[live] pid 会立即执行full gc

      ③老年代空间不足

     ④在执行minorgc时,判断老年代的最大连续空间是否可以保存所有的新生代对象,如果不足以保存,并且没有开启handlerPromotionFailure,直接fullGC, 如果开启了判断老年代的最大内存空间是否大于历史晋升的最大空间,如果小于进行fullGC.

    在以上基础上,先执行jstat -gcutil pid 2000 10 每2秒打印一次堆内存信息,连续打印10,根据打印结果判断老年代新生代使用率,

   接下来,dump出堆栈信息 -XX:+HeapDumpBeforeFullGC 然后在本地内存进行分析。

   jmap -histo:[live] pid 打印出堆内存当中对象数量和对象所占用的空间。

  再不添加live时,打印出来对象的数量和对象占用的内存空间,如果是添加live比没有添加live多出来了大量的请求而进入分配内存处理不过来,否则可能是其他原因造成的内存泄漏。

3、jvm在创建对象时,采用哪中内存分配的方式

     a) 指针碰撞法,假设java堆中内存时规整的,用过的放在一边,没有用过的放在另一边,中间有个指针作为指示器,在分配内存之前,只需要将指针移动对象大小的距离即可。

     b)空闲列表法,如果java堆当中内存不是连续的,虚拟机需要维护一个列表,来记录哪些内存空间是可以用的,在进行对象内存分配时,从空闲列表当中找到一块足够大的空间分配给对象

在使用Serial,ParNew等带有compact的垃圾回收算法的垃圾器中,使用的是指针碰撞法,在CMS, 标记清除算法当中,使用的是空闲列表法。

    分配内存时线程安全的问题,如何解决

    a) 采用CAS算法加上失败重试的方式,来保证原子性

    b) 本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),每个线程在java堆上预先分配一小块内存,当线程中需要分配空间时,先从本地线程缓冲区中分配,这个过程不用加锁,如果TLAB使用完了之后,并非配新的TLAB时,再采用加锁的方式。

4、对象的访问定位方式有哪些

    java程序需要栈上的reference数据来操作堆上的对象,虚拟机提供了两种方式

   a)使用句柄访问,java堆当中分配出来一块内存作为句柄池,reference当中保存的就是对象的句柄地址,句柄池当中包括两个部分,到对象实例数据的指针和对象类型数据的指针(对象类型保存在方法区)如下图

JVM问题总结_第1张图片

   b) 直接指针访问,reference指向的就是对象实例数据,如下图

    JVM问题总结_第2张图片

    句柄访问的好处就是,当对象发生移动的时候,只改变句柄池当中的实例数据指针即可,reference本身不需要修改。

    直接访问的好处就是,速度比较快。节省了一次指针定位的开销。hotspot采用直接定位的方式

 

你可能感兴趣的:(jvm)