JVM虚拟机——内存问题分析和寻找问题的工具介绍

内存溢出

内存溢出的原因:程序在申请内存时,没有足够的内存空间。

栈溢出

方法死循环递归调用(StackOverflowError)、不断建立线程(OutOfMemoryError)

堆溢出

不断创建对象,分配对象大于最大堆的大小(OutOfMemoryError)。
PS:GC效率过低时也会抛出OOM。

直接内存溢出

JVM分配的本地直接内存大小大于JVM的限制,也会出现OutOfMemoryError异常。

方法区溢出

在经常动态生产大量Class的应用中,CGLib字节码增强,动态语言,大量JSP(JSP第一次运行需要编译成Java类),基于OSGI的应用(同一个类,被不同的加载器加载也会设为不同的类)。

内存泄漏

程序在申请内存后,无法释放已申请的内存空间。

长生命周期的对象持有短生命周期对象的引用

例如将ArrayList设置为静态变量,则容器中的对象在程序结束之前将不能释放,从而造成内存泄漏。

连接未关闭

如数据库连接、网络连接和IO连接,只有连接被关闭后,垃圾收集器才会回收对应的对象。

变量作用域不合理

  • 一个变量的定义的作用范围大于其使用范围;
  • 如果没有及时的把对象设置为null。

内部类持有外部类

Java的非静态内部类的这种创建方式,会隐式的持有外部类的引用,并且是强引用。因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏。
解决方法:我们一般内部类定义为静态。如果不想定义为静态,则可以在内部类的内部显示持有一个外部类的软引用或弱引用,并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收。

Hash值改变

在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄漏。

内存泄漏和内存溢出比较

  • 内存溢出:实实在在的内存空间不足导致;
  • 内存泄漏:该释放的对象没有释放,导致可用内存减少。常见于使用容器保存元素的情况下;

如何避免:

  • 内存溢出:检查代码以及设置足够的空间;
  • 内存泄漏:一定是代码有问题。
    很多情况下,内存溢出是由内存泄漏导致的。

了解MAT

eclipse提供的一个分析内存泄漏的工具。

浅堆和深堆

浅堆:是指一个对象所消耗的内存。例如:在32位系统中,一个对象引用占用4个字节,一个int类型占用4个字节,long型变量会占据8个字节,每个对象头占用8个字节。
深堆:这个对象被回收后,可以真实释放的内存大小。
举个栗子:对象A引用了B、C和D,对象E引用了D,那么对象A的浅堆大小就是A本身,而如果A被回收,那么B和C都会被回收,所以A的深堆大小为A+B+C之和。而对象D还可以通过E访问到,所以不在A的深堆范围之内。

JDK为我们提供的工具

命令行工具

  • jps:列出当前机器上正在运行的虚拟机进程,JPS从操作系统的临时目录上去找(所以有一些信息可能显示不全)。

-q :仅仅显示进程,
-m:输出主函数传入的参数下的,就是在执行程序时从命令行输入的参数
-l: 输出应用程序主类完整package名称或jar完整名称.
-v: 列出jvm参数, -Xms20m -Xmx50m是启动程序指定的jvm参数

  • jstat:是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
    栗子:假设需要每250毫秒查询一次进程13616垃圾收集状况,一共查询10次,那命令应当是:jstat -gc 13616 250 10
    常用参数:

-class (类加载器)
-compiler (JIT)
-gc (GC堆状态)
-gccapacity (各区大小)
-gccause (最近一次GC统计和原因)
-gcnew (新区统计)
-gcnewcapacity (新区大小)
-gcold (老区统计)
-gcoldcapacity (老区大小)
-gcpermcapacity (永久区大小)
-gcutil (GC统计汇总)
-printcompilation (HotSpot编译统计)

  • jinfo:查看和修改虚拟机的参数。

jinfo –sysprops 可以查看由System.getProperties()取得的参数
jinfo –flag 未被显式指定的参数的系统默认值
jinfo –flags(注意s)显示虚拟机的参数
jinfo –flag +[参数] 可以增加参数,但是仅限于由java -XX:+PrintFlagsFinal –version查询出来且为manageable的参数
jinfo –flag -[参数] pid 可以修改参数

  • jmap:用于生成堆转储快照(一般称为heapdump或dump文件)。jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和jinfo命令一样,jmap有不少功能在Windows平台下都是受限的,除了生成dump文件的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris下使用。

jmap -dump:live,format=b,file=heap.bin < pid>
Sun JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。

  • jhat:jhat dump文件名后屏幕显示“Server is ready.”的提示后,用户在浏览器中键入http://localhost:7000/就可以访问详情。使用jhat可以在服务器上生成堆转储文件分析(一般不推荐,毕竟占用服务器的资源,比如一个文件就有1个G的话就需要大约吃一个1G的资源)。
  • jstack:(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。在代码中可以用java.lang.Thread类的getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个方法可以通过简单的几行代码就完成jstack的大部分功能,在实际项目中不妨调用这个方法做个管理员页面,可以随时使用浏览器来查看线程堆栈。

可视化工具

JMX(Java Management Extensions,即Java管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。
管理远程进程需要在远程程序的启动参数中增加:

-Djava.rmi.server.hostname=……
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

  • Jconsole
  • visualvm:下载地址:https://visualvm.github.io
    这是两个可视化的工具,使用起来比较简单。

GC的重要参数

  • 生产服务器推荐开启

-XX:-HeapDumpOnOutOfMemoryError 默认关闭,建议开启,在java.lang.OutOfMemoryError 异常出现时,输出一个dump.core文件,记录当时的堆内存快照。
-XX:HeapDumpPath=./java_pid< pid>.hprof 用来设置堆内存快照的存储文件路径,默认是java进程启动位置,。

  • 调优之前开启、调优之后关闭

-XX:+PrintGC
调试跟踪打印简单的GC信息参数:
-XX:+PrintGCDetails, +XX:+PrintGCTimeStamps
打印详细的GC信息
-Xlogger:logpath
设置gc的日志路径,如: -Xlogger:log/gc.log, 将gc.log的路径设置到当前目录的log目录下.
应用场景: 将gc的日志独立写入日志文件,将GC日志与系统业务日志进行了分离,方便开发人员进行追踪分析。

  • 考虑使用

-XX:+PrintHeapAtGC, 打印推信息
参数设置: -XX:+PrintHeapAtGC
应用场景: 获取Heap在每次垃圾回收前后的使用状况
-XX:+TraceClassLoading
参数方法: -XX:+TraceClassLoading
应用场景: 在系统控制台信息中看到class加载的过程和具体的class信息,可用以分析类的加载顺序以及是否可进行精简操作。
-XX:+DisableExplicitGC禁止在运行期显式地调用System.gc()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值