1.概述

JVM作为是JAVA中重要的基石,是java编程人员进阶路上的必需了解内容。为了帮助大家快速了解一些JVM的相关知识,本文将基于一个JVM案例(内存占比较高,调用垃圾回收方法后,内存占比仍然很高),来分析类似问题的解决方案以及排查思路。

2.JVM高内存占用案例

首先大概讲一下这个案例的基础现象:有一个JAVA应用程序,在经过多次垃圾回收之后,内存占用仍然很高。

针对上述案例,提供一种排查思路,具体如下(本文演示环境:idea,安装环境:jdk 1.8):

2.1 利用jps查看进程

jps(Java Virtual Machine Process Status Tool)是JDK提供的一个可以列出正在运行的Java虚拟机的进程信息的命令行工具,它可以显示JAVA虚拟机进程的执行主类(Main Class,main()函数所在的类)名称、本地虚拟机唯一ID(LVMID,Local Virtual Machine Identifier)等信息。注意,jps命令只显示它有访问权限的JAVA进程的信息。

jps一些指令信息:

指令 作用
-q 不显示主类名称、JAR文件名和传递给主方法的参数,只显示本地虚拟机唯一Id
-m 显示Java虚拟机启动时传递给main()方法的参数
-l 显示主类的完整包名,如果进程执行的是JAR文件,也会显示JAR文件的完整路径
-v 显示Java虚拟机启动时传递的JVM参数
-V 显示主类名称和本地虚拟机唯一Id,不显示JAR文件名和传递给主方法的参数
hostid 指定的远程主机,可以是ip地址和域名, 也可以指定具体协议,端口。如果不指定,则显示本机的Java虚拟机的进程信息
-help 显示jps命令的帮助信息
jps -[q] -[mlvV] -[hostid]
jps -[help]

注意:在没有指定任何参数的情况下,jps命令会显示每个Java虚拟机进程的本地虚拟机唯一ID,后面跟着主类名称或JAR文件名的简短形式。同时,指令中的m、l、v、V可以任意组合。

利用jps指令可以查看当前JAVA虚拟机正在运行的进程,对于本地虚拟机来说,本地虚拟机唯一ID和操作系统的进程ID(PID,Process Identifier)是一致的,如果同时启动多个Java虚拟机进程,无法根据进程名称确定某个进程,我们就是使用jps命令显示主类名称的功能区分出来。
由于本文的测试环境是本地环境,因此可以先用jps指令查看一下虚拟机当前运行的JAVA进程,如下:

本文的测试代码写在JvisiualTest类中,因此需要重点排查该类。如果在centOS环境下遇到内存大量占用的情况,可以先用top指令查看进程id和内存占用情况。

2.2 利用jmap指令查看堆栈信息

jmap(Java Memory Map)是jdk安装后自带的一些小工具,主要用于打印指定JAVA进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。
利用如下指令展示pid的整体堆信息:

jmap -heap pid


下面仔细分析一下每个显示属性的具体意义:

Heap Configuration: //堆内存初始化配置MinHeapFreeRatio         = 0  //设置JVM堆最小空闲比率(默认40),对应jvm启动参数-XX:MinHeapFreeRatioMaxHeapFreeRatio         = 100  //设置JVM堆最大空闲比率(默认70),对应jvm启动参数-XX:MaxHeapFreeRatioMaxHeapSize              = 1864368128 (1778.0MB) //设置JVM堆的最大值,对应jvm启动参数-XX:MaxHeapSize=NewSize                  = 38797312 (37.0MB)  //设置JVM堆的“新生代”的默认大小,对应jvm启动参数-XX:NewSize=MaxNewSize               = 621281280 (592.5MB) //设置JVM堆的‘新生代’的最大值,对应jvm启动参数-XX:MaxNewSize=OldSize                  = 78643200 (75.0MB) //设置JVM堆的“老生代”的大小,对应jvm启动参数-XX:OldSize=NewRatio                 = 2 //“新生代”和“老生代”的大小比率,对应jvm启动参数-XX:NewRatio=SurvivorRatio            = 8 //设置年轻代中Eden区与Survivor区的大小比值,对应jvm启动参数-XX:SurvivorRatio=MetaspaceSize            = 21807104 (20.796875MB) //Metaspace扩容时触发FullGC的初始化阈值,也是最小的阈值,对应jvm启动参数-XX:MetaspaceSizeCompressedClassSpaceSize = 1073741824 (1024.0MB) //Java8在UseCompressedOops之外,额外增加了一个新选项叫做UseCompressedClassPointer。这个选项打开后,class信息中的指针也用32bit的Compressed版本。而这些指针指向的空间被称作“Compressed Class Space”。默认大小是1G,但可以通过“CompressedClassSpaceSize”调整MaxMetaspaceSize         = 17592186044415 MB //设置元空间Metaspace最大值,对应jvm启动参数-XX:MaxMetaspaceSizeG1HeapRegionSize         = 0 (0.0MB) // 使用G1收集器时,它将整个Java堆划分成约2048个大小相同的独立Region块,每个Region块大小根据堆空间的实际大小而定,整体被控制在1MB到32MB之间,且为2的N次幂,即1MB,2MB, 4MB, 8MB, 1 6MB, 32MB。可以通过-XX :G1HeapRegionSize设定。所有的Region大小相同,且在JVM生命周期内不会被改变Heap Usage: //堆内存使用情况
PS Young Generation
Eden Space: //Eden区内存分布capacity = 58720256 (56.0MB) //Eden区总容量used     = 45173512 (43.08081817626953MB) //Eden区已使用free     = 13546744 (12.919181823730469MB) //Eden区剩余容量76.93003245762416% used //Eden区使用比率
From Space: //其中一个Survivor区的内存分布capacity = 4718592 (4.5MB)used     = 4424216 (4.219261169433594MB)free     = 294376 (0.28073883056640625MB)93.76135932074652% used
To Space: //另一个Survivor区的内存分布capacity = 26214400 (25.0MB)used     = 0 (0.0MB)free     = 26214400 (25.0MB)0.0% used
PS Old Generation //当前的Old区内存分布capacity = 254279680 (242.5MB) //总容量used     = 161013136 (153.55409240722656MB) //已使用free     = 93266544 (88.94590759277344MB) //剩余容量63.32127521947487% used //使用率

2.3 利用jconsole查看堆内存信息

jconsole是jdk自带的监控工具,它用于连接正在运行的本地或者远程的JVM,对运行在java应用程序的资源消耗和性能进行监控,并利用可视化图表的形式提供监控界面,方便实时监控内存、堆栈信息等,同时该指令占用服务器的内存很小。
在idea中执行如下指令:

会得到如下结果:

执行GC后出现以下结果:

由上图可知,执行GC命令后,内存被回收了一部分,但是回收部分很小,内存并没有太大变化,说明大部分对象可能存在于老年代或永久代中,可以利用jvisualvm可视化方式来查看具体堆栈信息。

2.4 利用jvisualvm查看堆栈信息

jvisualvm是可以监控java运行内存的可视化工具,能够直观查看正在运行的JAVA服务内存信息、堆栈信息等,下面将利用该指令来查看一些关键信息。

执行上述命令会得到如下结果:


连接到异常的线程,可以得到如下堆内存信息:

抓取堆内存当前快照信息,得到结果如下:


得到当前堆内存快照信息如下:

由上图可知,其中占用内存最大的是一个ArrayList,内存大概209M,具体如下:

查看该数组可以发现,该ArrayList内部包含了200个Product对象,product对象内部有一个1048字节的对象,这些应该就是内存占用的原因:

2.5 源码分析

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;public class JvisiualTest {public static void main(String[] args) throws InterruptedException {List<Product> lists = new ArrayList<>();for (int i = 0; i < 200; i++) {lists.add(new Product());}TimeUnit.SECONDS.sleep(10000000);}
}class Product {private byte[] big = new byte[1024 * 1024];}

由上述代码可知,Product对象内部只有一个1M的对象,测试代码启动时,会生成200个Product对象,并放入ArrayList数组中,然后线程进入休眠状态,此时数组中的Product对象一直未被销毁,一直存在于老年代中,导致内存一直处于高占用状态。

3.小结

1.jps是一个实用指令,日常开发中可以查看java运行进程信息;
2.jmap可以打印指定JAVA进程的堆内存信息;
3.jconsole和jvisualvm是利用可视化的方式来查看堆内存信息及一些进程信息。

4.参考文献

1.https://www.jianshu.com/p/c52ffaca40a5
2.https://www.jianshu.com/p/b448c21d2e71
3.https://www.jianshu.com/p/5ee71f1724cd

JAVA从入门到放弃之JVM内存高占用问题排查相关推荐

  1. Java从入门到放弃09---多态/向上转型/向下转型/多态内存图/抽象类/关键字abstract不能和哪些关键字共存/接口/类与类,类与接口,接口与接口的关系/抽象类与接口的区别

    Java从入门到放弃09-多态/向上转型/向下转型/多态内存图/抽象类/关键字abstract不能和哪些关键字共存/接口/类与类,类与接口,接口与接口的关系/抽象类与接口的区别 01 多态 多态指的是 ...

  2. java从入门到放弃(二)

    java从入门到放弃(二) //求园面积 结果保留5位小数double ymj = Math.PI*Math.pow(``2.14``,` `2``);DecimalFormat df1 =new D ...

  3. Java从入门到放弃-序言

    Java从入门到放弃 前言 本人希望由浅及深的探讨java的底层原理,和编程思想,与大家一起学习提升对程序语言的认知.由于自己是理工科出身,所以对底层原理往往非常感兴趣.那么就跟我一起学习Java吧. ...

  4. mysql 停止服务内存_服务器莫名的内存高占用 导致 MySQL 停止运行问题

    这问题是年后开始出现的,服务器内存占用越来越高,一度达到90%,最后 MySQL 都停止运行了.贴吧签到的数据库用户 ID 这一项也丢失了,导致无法签到,断签了好些天,被提醒才发现,要挨打的.幸好设置 ...

  5. 转:JAVA常见错误处理方法 和 JVM内存结构

    OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemor ...

  6. JAVA常见错误处理方法 和 JVM内存结构

    OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemor ...

  7. JVM(Java虚拟机)详解(JVM 内存模型、堆、GC、直接内存、性能调优)

    JVM(Java虚拟机) JVM 内存模型 结构图 jdk1.8 结构图(极简) jdk1.8 结构图(简单) JVM(Java虚拟机): 是一个抽象的计算模型. 如同一台真实的机器,它有自己的指令集 ...

  8. jvm内存溢出区域和排查方法

    目录 1.堆溢出 2.栈溢出 3.方法区和运行时常量池溢出 4.本机直接内存溢出 首先我们需要掌握什么是内存溢出和内存泄漏 内存泄漏:即声明的对象无法被回收,一直存在于内存中,使得占用的内存就像被泄漏 ...

  9. 《Java从入门到放弃》JavaSE入门篇:文件操作

    Java中的文件操作还有点小复杂··· 不过没关系,我会把它讲得很简单,嘿嘿嘿!!! 在讲Java中的文件操作前,先了解一个概念--"流",比如我们把一个杯子的水倒到另一个同样大小 ...

  10. 《Java从入门到放弃》JavaSE入门篇:面向对象语法二(入门版)

    想了半天,发现单独的封装和多态没什么好讲的,我们就简单说说Java里面对应的语法吧. 相关内容如下: 一.访问修饰符 二.getter/setter方法 三.构造方法 四.super和this 五.s ...

最新文章

  1. 利用OpenCV的VideoWriter类实现视频的写操作
  2. 2018-2019 1 20165203 实验五 通用协议设计
  3. 微创社001期:从0开始创作第一本技术书
  4. go 查看全局安装了哪些包_如何用 GVM 管理 Go 项目
  5. C语言--第2次作业
  6. Java JDK 10:下一代 Java 有哪些新特性?
  7. 微博html怎么编辑器,制作一个微博文本编辑器
  8. abaqus实例手册_《ABAQUS 6.14超级学习手册》——1.5 ABAQUS帮助文档
  9. 《数值分析》-- 数值计算中的误差与有效数字
  10. 正从服务器获取安装包消息 荣耀9,华为荣耀9 root教程 华为荣耀9获取root权限的方法...
  11. matlab如何定义dmod函数,matlab中的dmod函数
  12. centos7的scp命令_Linux命令-CentOS7安装scp命令,进行mac与Linux之间的文件上传下载...
  13. VScode退出全屏
  14. 高德地图ar步行导航使用教程分享
  15. 免费linux虚拟空间,linux免费虚拟主机(linux搭建虚拟主机)
  16. JAVA实现Doc与Docx互转
  17. 学习百度Apollo中的决策规划
  18. 王爽 汇编语言第二版 课程设计2
  19. elf文件不能执行的原因
  20. 医院门诊预约挂号小程序模板

热门文章

  1. [渝粤教育] 中国地质大学 信息检索 复习题 (2)
  2. 如何将360浏览器兼容IE8、IE7
  3. FastReport 2021版中文手册PDF下载
  4. 简单聊聊离散数学是什么
  5. 计算机多媒体软件应用,计算机应用基础【多媒体软件应用】课件.ppt
  6. java碰碰球历险记下载_幼儿园玩球教案碰碰球.doc
  7. mysql 拖库_【渗透测试】温故知新之拖库七种方法
  8. 没有找到MSVCR100.dll解决方法
  9. 【PS】制作透明质感按钮
  10. php网页拍照并上传,HTML中网页拍照并上传照片的实现方法