JVM 第五节:JVM 性能优化(上)

内存溢出

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

栈溢出

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

堆溢出

不断创建对象,分配对象大于最大堆的大小(OutOfMemoryError)

直接内存

JVM 分配的本地直接内存大小大于 JVM 的限制(可以通过-XX:MaxDirectMemorySize 来设置(不设置的话默认与堆内存最大值一样,也会出现

OOM 异常)

方法区溢出

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

内存泄漏

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

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

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

连接未关闭

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

变量作用域不合理

例如,1.一个变量的定义的作用范围大于其使用范围,2.如果没有及时地把对象设置为 null

内部类持有外部类

Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏
如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏(你认为垃圾回收器会回收掉外部类的实例,但由于内部类持有外部类的引用,导致垃圾回收器不能正常工作)
解决方法:你可以在内部类的内部显示持有一个外部类的软引用(或弱引用),并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收;

Hash 值改变

在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露(有代码案例 Node 类)

内存泄漏和内存溢出辨析

内存溢出:实实在在的内存空间不足导致;
内存泄漏:该释放的对象没有释放,常见于使用容器保存元素的情况下。
如何避免:
内存溢出:检查代码以及设置足够的空间
内存泄漏:一定是代码有问题

往往很多情况下,内存溢出往往是内存泄漏造成的。

了解 MAT

浅堆和深堆

浅堆 :(Shallow Heap)是指一个对象所消耗的内存。例如,在 32 位系统中,一个对象引用会占据 4 个字节,一个 int 类型会占据 4 个字节,long 型变量会占据 8 个字节,每个对象头需要占用 8 个字节。
深堆 :这个对象被 GC 回收后,可以真实释放的内存大小,也就是只能通过对象被直接或间接访问到的所有对象的集合。通俗地说,就是指仅被对象所持有的对象的集合。深堆是指对象的保留集中所有的对象的浅堆大小之和。
举例:对象 A 引用了 C 和 D,对象 B 引用了 E。那么对象 A 的浅堆大小只是 A 本身,而如果 A 被回收,那么 C 和 D都会被回收(可达性分析算法),所以 A的深堆大小为 A+C+D 之和,同时由于对象 E 还可以通过对象 B 访问到,因此不在对象 A 的深堆范围内
JVM 第五节:JVM 性能优化(上)JVM 第五节:JVM 性能优化(上)JVM 第五节:JVM 性能优化(上)incoming 和 outgoing
JDK为我们提供的工具
命令行工具

jps

列出当前机器上正在运行的虚拟机进程,JPS 从操作系统的临时目录上去找(所以有一些信息可能显示不全)。
-q :仅仅显示进程,
-m:输出主函数传入的参数. 下的 hello 就是在执行程序时从命令行输入的参数
-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 查 询 出 来 且
JVM 第五节:JVM 性能优化(上)为 manageable 的参数

jinfo –flag -[参数] pid 可以修改参数Thread.getAllStackTraces();
1.程序运行时只打印简单 GC

2.通过 jinfo修改 参数,打印 GC 详情

JVM 第五节:JVM 性能优化(上)

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 生成的堆转储快照。

JVM 第五节:JVM 性能优化(上)

jhat

jhat dump 文件名
后屏幕显示“Server is ready.”的提示后,用户在浏览器中键入 http://localhost:7000/就可以访问详情

JVM 第五节:JVM 性能优化(上)

使用 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

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

visualvm

插件中心地址
但是注意版本问题,不同的 JDK 所带的 visualvm 是不一样的,下载插件时需要下对应的版本。
一般来说,这个工具是本机调试用,一般生产上来说,你一般是用不了的(除非启用远程连接)
JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

JVM 第五节:JVM 性能优化(上)

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()