专栏首页java技术大本营JVM|03内存溢出实战

JVM|03内存溢出实战

实战 内存溢出定位与分析

环境搭建

/**
 * 模拟测试插入一百万条字符串[image.png](https://img.hacpai.com/file/2019/08/image-dd10de62.png)                str += UUID.randomUUID().toString();
            }
            list.add(str);
        }
        System.out.println("ok");
    }
}

结果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid10272.hprof ...
Heap dump file created [8323049 bytes in 0.028 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at cn.jeff.test.Test01.main(Test01.java:18)

Process finished with exit code 1

参数中设置了输出dump文件,默认输出在项目根目录下。

问题分析(基于MAT):

可以看出87.93%的内存被Object[]数组占用了,通常情况下不会出现这么高的占用 再去看详细的数据情况:

可以看出在Object[]数组中装满了刚才生产的UUID,导致了最后的系统内存溢出问题。

分析线程执行情况:jstack

线程知识回顾

线程的六种状态

  1. 初始态(NEW) 创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。
  2. 运行态(RUNNABLE),在Java中,运行态包括 就绪态 和 运行态。 2.1 就绪态 : 该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运 行。 所有就绪态的线程存放在就绪队列中。 2.2 运行态 : 获得CPU执行权,正在执行的线程。 由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条 运行态的线程。
  3. 阻塞态(BLOCKED) 当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。 而在Java中,阻塞态专指请求锁失败时进入的状态。 由一个阻塞队列存放所有阻塞态的线程。 处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执 行。
  4. 等待态(WAITING) 当前线程中调用wait、join、park函数时,当前线程就会进入等待态。 也有一个等待队列存放所有等待态的线程。 线程处于等待态表示它需要等待其他线程的指示才能继续运行。 进入等待态的线程会释放CPU执行权,并释放资源(如:锁)
  5. 超时等待态(TIMED_WAITING) 当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就 会进入该状态; 它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其 他线程唤醒; 进入该状态后释放CPU执行权 和 占有的资源。 与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。
  6. 终止态(TERMINATED) 线程执行结束后的状态。

实战:死锁问题

构建死锁环境

public class TestDeadLock {

    //    定义两个锁
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();

    public static void main(String[] args) {
//        创建初始态线程
        Thread thread1 = new Thread(new Thread1());
        Thread thread2 = new Thread(new Thread2());
//        就绪态
        thread1.start();
        thread2.start();

    }

    //    第一个线程
    private static class Thread1 implements Runnable {
        @Override
        public void run() {
            synchronized (obj1) {
//                此时得到了obj1这个锁
                System.out.println("Thread1得到了obj1这把锁!");
                try {
//                停下来休息两秒 为了让Thread2得到obj2这把锁 造成死锁 sleep方法是不会释放锁的
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj2) {
                    System.out.println("Thread1得到了obj2这把锁!");
                }
            }
        }
    }

    //    第二个线程
    private static class Thread2 implements Runnable {
        @Override
        public void run() {
//        得到obj2这把锁
            synchronized (obj2) {
                System.out.println("Thread2得到了obj2这把锁!");
                try {
//            为了让obj1被先得到
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//            得到obj1
                synchronized (obj1) {
                    System.out.println("Thread2得到了obj1这把锁!");
                }
            }
        }
    }
}

结果

[root@hadoop101 jvm]# javac TestDeadLock.java
[root@hadoop101 jvm]# java TestDeadLock
Thread1得到了obj1这把锁!
Thread2得到了obj2这把锁!
# 程序卡在这里

保持程序的运行状态,另外创建一个xshell命令行窗口

# 通过jps找到运行的TestDeadLock程序的端口
[root@hadoop101 ~]# jps
2956 TestDeadLock
3038 Jps
1967 Bootstrap
# 指令格式:jstack 端口号
[root@hadoop101 ~]# jstack 2956
2019-08-16 02:43:12
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):

"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007ffa54001000 nid=0xbf4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007ffa7c008800 nid=0xb8d waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #9 prio=5 os_prio=0 tid=0x00007ffa7c0ca800 nid=0xb97 waiting for monitor entry [0x00007ffa6c51d000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at TestDeadLock$Thread2.run(TestDeadLock.java:52)
- waiting to lock <0x00000000e345be78> (a java.lang.Object)
- locked <0x00000000e345be88> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)

"Thread-0" #8 prio=5 os_prio=0 tid=0x00007ffa7c0c9000 nid=0xb96 waiting for monitor entry [0x00007ffa6c61e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at TestDeadLock$Thread1.run(TestDeadLock.java:31)
- waiting to lock <0x00000000e345be88> (a java.lang.Object)
- locked <0x00000000e345be78> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)

"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007ffa7c0b3000 nid=0xb94 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007ffa7c0b0000 nid=0xb93 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007ffa7c0ad800 nid=0xb92 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007ffa7c0ac000 nid=0xb91 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007ffa7c079000 nid=0xb90 in Object.wait() [0x00007ffa6cc24000]
   java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3408e98> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000e3408e98> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007ffa7c074800 nid=0xb8f in Object.wait() [0x00007ffa6cd25000]
   java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3406b40> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000e3406b40> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=0 tid=0x00007ffa7c06d000 nid=0xb8e runnable

"VM Periodic Task Thread" os_prio=0 tid=0x00007ffa7c0b6000 nid=0xb95 waiting on condition

JNI global references: 6


Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007ffa600062c8 (object 0x00000000e345be78, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007ffa60004e28 (object 0x00000000e345be88, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
at TestDeadLock$Thread2.run(TestDeadLock.java:52)
- waiting to lock <0x00000000e345be78> (a java.lang.Object)
- locked <0x00000000e345be88> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at TestDeadLock$Thread1.run(TestDeadLock.java:31)
- waiting to lock <0x00000000e345be88> (a java.lang.Object)
- locked <0x00000000e345be78> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)

# 系统提示发现一个死锁
Found 1 deadlock.

死锁分析:

"Thread-1" #9 prio=5 os_prio=0 tid=0x00007ffa7c0ca800 nid=0xb97 waiting for monitor entry [0x00007ffa6c51d000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at TestDeadLock$Thread2.run(TestDeadLock.java:52)
- waiting to lock <0x00000000e345be78> (a java.lang.Object)
- locked <0x00000000e345be88> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)

"Thread-0" #8 prio=5 os_prio=0 tid=0x00007ffa7c0c9000 nid=0xb96 waiting for monitor entry [0x00007ffa6c61e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at TestDeadLock$Thread1.run(TestDeadLock.java:31)
- waiting to lock <0x00000000e345be88> (a java.lang.Object)
- locked <0x00000000e345be78> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)

可以看出: Thread-1:正在手握着0x00000000e345be88这个锁,等待着0x00000000e345be78的获取; Thread-0:正在手握着0x00000000e345be78这个锁,等待着0x00000000e345be88的获取;

VisualVM工具的使用

简介

VisualVM,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的)。 VisualVM使用简单,几乎0配置,功能还是比较丰富的,几乎囊括了其它JDK自带命令的所有功能。 1、内存信息 2、线程信息 3、Dump堆(本地进程) 4、Dump线程(本地进程) 5、打开堆Dump。堆Dump可以用jmap来生成。 6、打开线程Dump 7、生成应用快照(包含内存信息、线程信息等等) 8、性能分析。 9、CPU分析(各个方法调用时间,检查哪些方法耗时多) 10、内存分析(各类对象占用的内存,检查哪些类占用内存多

使用方法

启动

VisualVM在jdk的安装目录的bin下面有jvisualvm.exe,打开便可使用。

本文分享自微信公众号 - java技术大本营(java-ideashare),作者:JEFF

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JVM | OpenJDK源码执行HelloWorld

    平时我们在运行java程序时, 一般有两种选择, 1是在服务器上直接用java -jar 命令来运行程序, 2是在idea中直接点击运行/debug,如下图所示...

    微笑的小小刀
  • Java整理篇(一)

    古人曰:做事要总结。对,借着古人这句话,我们总结下这一个星期,一个星期时间不长,但实际上群里积累的知识点挺多的,基本上把java基础的都过了一遍,如输入输出,...

    微笑的小小刀
  • java学习原理篇|java程序运行套路

    ? :寺水, 你说, 我们都是一样的吗? ? :从正态分布来看的话,除去前面和后面的,大多数人,生来差别并不大,可以简单认为,在出生那一刻是平等的. 人...

    微笑的小小刀
  • 性能优化-jstack的使用

    有些时候我们需要查看下jvm中的线程执行情况,比如,发现服务器的CPU的负载突然增高了、出现了死锁、死循环等,我们该如何分析呢?

    cwl_java
  • JDK 定时任务 Timer 与 ScheduledExecutorService 排坑记录

    正在认真敲代码的楼主,突然收到数据维护系统发过来的报警邮件说楼主凌晨跑的定时任务没有成功,于是便开始了楼主今天的找坑填坑的过程。

    haifeiWu
  • GC之jstack 原

        用jstack命令jstack -l 18261>./18261jstack.txt拉取线程信息,18261是进程ID,文件18261jstack.tx...

    克虏伯
  • Maven3问题记录-1

    M3_HOME=D:\ProgramFiles\apache-maven-3.0.3

    py3study
  • 记一次 Istio 云数据库连接失败的错误排查过程

    写这篇文章的目的主要是记录 在 容器/istio 下如何使用一些手段和工具来排查错误。

    谢正伟
  • Spring杂谈 | 从桥接方法到JVM方法调用

    这些代码对我之前也造成了不小疑惑,在彻底弄懂后通过本文分享出来,也能减少大家在阅读代码过程中的障碍!

    程序员DMZ
  • Android深入四大组件(四)广播的注册、发送和接收过程

    前言 我们接着来学习Android四大组件中的BroadcastReceiver,广播主要就是分为注册、接收和发送过程。建议阅读此文前请先阅读Android深入...

    用户1269200

扫码关注云+社区

领取腾讯云代金券