1-JVM内存分配
1.1-JVM内存分配性能问题
JVM内存分配不合理最直接的表现就是频繁的GC,这会导致上下文切换等性能问题,从而降低系统的吞吐量、增加系统的响应时间。因此,如果你在线上环境或性能测试时,发现频繁的GC,且是正常的对象创建和回收,这个时候就需要考虑调整JVM内存分配了,从而减少GC所带来的性能开销。
1.2-对象在堆中的生存周期
在JVM内存模型的堆中,堆被划分为新生代和老年代,新生代又被进一步划分为Eden区和Survivor区,最后Survivor由From Survivor和To Survivor组成。
当我们新建一个对象时,对象会被优先分配到新生代的Eden区中,这时虚拟机会给对象定义一个对象年龄计数器(通过参数-XX:MaxTenuringThreshold设置)。
当Eden空间不足时,虚拟机将会执行一个新生代的垃圾回收(Minor GC)。这时JVM会把存活的对象转移到Survivor中,并给对象的年龄+1。对象在Survivor中同样也会经历MinorGC,每经过一次MinorGC,对象的年龄将会+1。
参数-XX:PetenureSizeThreshold设置直接被分配到老年代的最大对象,这时如果分配的对象超过了设置的阀值,对象就会直接被分配到老年代,这样做的好处就是可以减少新生代的垃圾回收。
1.3-查看JVM堆内存分配
java -pf | grep java:查询当前的java进程;也可以使用jps查看
jmap -heap pid:查看对应进程的内存信息
在JDK1.7中,默认情况下年轻代和老年代的比例是1:2,我们可以通过–XX:NewRatio重置该配置项。年轻代中的Eden和To Survivor、From Survivor的比例是8:1:1,我们可以通过-XX:SurvivorRatio重置该配置项。
1.4-JVM GC日志
在实际的项目中,我们一般先配置默认的JVM参数,并且配置好gc日志路径,如果发现系统响应慢或者吞吐量上不去,我们可以通过分析GC日志来查找问题。
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/log/gc.log
-XX:PrintGCTimeStamps:打印GC具体时间;
-XX:PrintGCDetails :打印出GC详细日志;
-Xloggc: path:GC日志生成路径。
2-GC问题定位查找步骤
1.配置和设置好JVM参数,按照自己服务器实际配置一个基本参数;
java -jar -Xms2048m -Xmx2048m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-Xloggc:/tmp/heapTest.log
abc.jar
2.当出现吞吐量明显下降,系统响应超时后;
3.需要把gc日志下载到本地,然后使用一些工具来分析,比如我们上节提到的GCViewer工具打开看,可以查看GC次数和内存使用情况。
4.根据参数分析,我们重新调整GC参数,根据项目的实际情况来进行一些设置。比如增大堆内存大小,加大新生代大小等等措施。最主要的目的是减少FullGC和减少MinorGC次数。
3-内存持续上升或者CPU问题定位
1.通过 top 命令找到占用内存/cpu最高的 pid[进程id]
2.通过 top -Hp pid 查看进程中占用内存/cpu过高的 tid[线程id]
3.通过 printf '%x/n' tid 把线程id转化为十六进制
4.通过 jstack pid | grep tid -A 30 定位线程堆栈信息
其实我们也可以采用arthas。arthas是阿里一款开源java性能诊断工具,除了java自带的jmap命令没有实现之外,其它的命令都实现,并且功能强大。
jvm查看java进程相关信息
thread
thead:列出当前进程所有线程信息
thread | grep http-nio-:过滤http-nio-名称的线程,如果有自己的线程池,可以查询出来
thread 29:查询29号线程具体信息
thread -b:查询死锁相关信息
类似sc,sm,redefine命令,功能很强大,很多命令可以看,实战中完全可以执行help看各种命令说明,redefine可以动态修改class,直接替换class文件
如果需要分析dump文件可以用jdk自带的jvisualvm.exe,不一定非要用MAT。