几个jvm问题场景

本文详细探讨了Java JVM中常见的问题,包括OOM(内存溢出)、死循环、内存泄漏、堆内存小、StackOverflowError以及CPU占用过高。通过案例分析和排查方法,帮助理解问题产生的原因及解决思路。
摘要由CSDN通过智能技术生成


之前介绍过一些关于jvm的一些知识,包括内存结构,垃圾回收算法,垃圾回收器,这次主要是模拟一些问题场景,通过一些问题场景的分析,可以对这几部分的理解更加深刻。而且顺带可以简单的介绍一些jvm调试的命令,以及运行参数,方便我们去排查问题。

OOM

oom翻译过来是Out Of Memory,也就是内存溢出,Java中的报错是java.lang.OutOfMemoryError,这个应该是我们比较常见的一个问题场景了,我觉得OOM的出现,不论最终详细的异常信息是什么, 一般都可以分为两种情况,一种就是我们代码的逻辑有问题,一般都是各种死循环引起的。一种是真的内存不够了。首先说一下,逻辑有问题的部分,这种情况要完全杜绝,找到合适的循环终止逻辑,是最基本的操作。

死循环

内存溢出的问题很好模拟,只需要做两步,第一步调小JVM的堆内存,第二步就是不停的创建大对象,去填充堆空间。这两步就可以了,看一段代码

package cn.yarne;

public class Main {
    public static void main(String[] args) {
        while (true){
          int[] bigArray=new int[1024*1024];
        }
    }
}

然后我们使用-Xmx10m -Xms100m这两个参数将堆内存调制固定10m,这段代码执行会溢出吗?答案是不会,为啥呢?其实我们之前有说过,就是堆内存不够的时候,会进行GC,我们只是不停的创建对象,但是对象没有被实际使用,虽然在一直创建,但是这些对象也在一直回收,所以不会出现内存溢出。我们使用-XX:+PrintGCDetails参数打印出日志看下

[PSYoungGen: 0K->0K(2560K)] 4720K->4720K(9728K), 0.0001753 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 4720K->4720K(9728K), 0.0001518 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 4720K->624K(7168K)] 4720K->624K(9728K), [Metaspace: 3277K->3277K(1056768K)], 0.0014731 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 4720K->4720K(9728K), 0.0001651 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 4720K->4720K(9728K), 0.0001711 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 4720K->624K(7168K)] 4720K->624K(9728K), [Metaspace: 3277K->3277K(1056768K)], 0.0014868 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

仔细观察上边(当然哈,只保留了一点,下边都输重复的YGC,YGC,FULLGC,日志一样,我就给都删了)的输出,可以看出,

  1. 因为数组对象比较大,所以数组对象直接进入了老年代,年轻代每次回收都是0-0 [PSYoungGen: 0K->0K(2560K)]
  2. 因为内存比较小,会频繁FullGC,每两次YoungGC就进行一次FullGC
  3. 虽然频繁GC,但是一直没有出现内存溢出,因为每次老年代都能将无用的数组对象都清掉 [ParOldGen: 4720K->624K(7168K)]

下边将代码稍微做调整。

package cn.yarne;

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List list=new ArrayList();
        while (true){
          int[] bigArray=new int[1024*1024];
            list.add(bigArray);
        }
    }
}

看下输出

[GC (Allocation Failure) [PSYoungGen: 1801K->488K(2560K)] 5897K->4816K(9728K), 0.0011265 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 488K->496K(2560K)] 4816K->4864K(9728K), 0.0009545 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 496K->0K(2560K)] [ParOldGen: 4368K->4718K(7168K)] 4864K->4718K(9728K), [Metaspace: 3262K->3262K(1056768K)], 0.0058792 secs] [Times: user=0.08 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 4718K->4718K(9728K), 0.0007476 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at cn.yarne.Main.main(Main.java:10)
[PSYoungGen: 0K->0K(2560K)] [ParOldGen: 4718K->4701K(7168K)] 4718K->4701K(9728K), [Metaspace: 3262K->3262K(1056768K)], 0.0060607 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2560K, used 101K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd19428,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 7168K, used 4701K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 65% used [0x00000000ff600000,0x00000000ffa974e0,0x00000000ffd00000)
 Metaspace       used 3307K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 362K, capacity 388K, committed 512K, reserved 1048576K

这就很明显了,当第一次FullGC的时候,老年代不仅空间没有变小,而且还变多了,然后放下下,紧接着就OOM,Error之后jvm不进行处理,直接异常退出。虽然这是一个非常简单的例子,但是可以让我们对整个jvm可以有更多了解

内存泄漏

内存泄漏也算是逻辑问题的一种,平时在正常的时候可能不会很明显,但是当进行大量操作,伴随着内存泄漏的话,就会出现OOM的问题,我们上边说的问题是一直内存空间占用累加无法释放,其实内存泄漏也是这个问题,比如我之前说过的ThreadLocal,只要我们有一种操作下,存在了遗漏的内存碎片,无法被回收,那OOM是早晚的事情,这部分就不进行代码演示,理解就可以

堆内存小

上边说的问题都是我们写代码过程中出现的问题,而且这个内存小的问题呢,其实就是两种,一种是物理机内存小,那实在没办法,另一种是堆内存小,一般因为我们如果不配置的话,堆内存是物理机内存的四分之一,如果物理机本来内存就小,那一般来说都不够用,所以我们可以用上边提到过的-Xmx10m -Xms10m去手动控制堆内存的大小,一般来说,我们手动将这个内存调大,其实就会解决大部分的问题

StackOverflowError

栈溢出的意思就是创建的栈帧太深了,超出了栈大小,一般可能因为我们使用递归这种逻辑,如果没有一个合理的终止条件,或者终止条件太深,就会造成栈溢出,下面用一个最简单的例子模拟一下

package cn.yarne;

public class Main {
    public static void main(String[] args) {
        run();
    }
    public static void run(){
        run();
    }
}

这是个最简单的使用有问题的例子,当然我们真的会回到一种情况就是我们需要深一些的栈来处理一些复杂或者重复的工作,那我们可以使用-Xss1m来规定每个栈的固定大小

CPU占用过高

程序运行期间,CPU频繁占用过高,可能就需要我们花一些时间去进行排查,一般来说,有两种情况,一种是FullGC频繁,导致GC与应用线程并发进行,导致CPU占用过高一种是我们程序内部真的有非常消耗CPU的操作但是不论是哪种情况,我们都需要去进行定位,找到最终造成CPU占用过高的原因,从而判断影响范围,排查此类问题的思路一般是

  1. 先使用top命令查询cpu占用比较高的java进程,也可以安装htop,属于top的升级版
  2. 使用top -Hp pid命令查询当前进程内,线程情况,可以找出CPU占用最高的线程
  3. 使用printf "%x\n" tid命令,将当前线程tid号转化成可以供我们查看堆栈追踪的十六进制的线程号
  4. 找出线程号之后,使用jstack pid | grep tid -A 30可以打印出堆栈信息,也可以使用>将堆栈输出到文件中,方便查看
  5. 然后使用之前转化出来十进制线程号去搜索堆栈信息,跟踪我们代码的执行情况,看看是那块的代码
  6. 排查代码,看看是否有逻辑问题,或者大对象等等

当然,我们也可以根据gc的回收过程,观察一下是不是因为GC频繁FullGC导致

1. `jps -v` 可以看当前在运行着的所有java进程
2. `jmap -heap pid` 可以查看当前jvm堆的详细信息,包括堆大小,gc回收器,当前堆各个空间的大小以及占用情况
3. `jmap -dump:format=b,file=/usr/aaa.log {pid} ` 可以将当前堆信息dump到文件中,进行工具分析
4. `jstat -gcutil pid` 可以查看当前gc对各个区域的回收情况
5. `jstat -gc pid 5000` 每五秒一次持续打印gc回收的信息

根据gc回收的信息,可以判断是否因为频繁FullGC导致系统慢,配合上边的流程进行排查

总结

文章简单介绍了jvm经常遇到的几个问题场景,包括最基础的排查方式,当然还有很多的情况没有说到,只是留一个大概的思路,让我们知道如果遇到问题,可以用什么方式排查,思路以及工具,比如java 中 bin目录下有非常多的自带的排查工具,哪怕有些用不到,我们也要对这些命令有一些大概的了解,当需要的时候,可以第一时间想到

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值