JVM性能调优-内存溢出与泄露
内存溢出
原因
申请内存时,没有足够的内存空间
内存溢出的方式
- 栈溢出(栈的默认大小是1M)
- 堆溢出
- 方法区溢出
- 本地内存溢出:分配的本地内存大小大于JVM 的限制
内存泄露
原因
申请内存后,无法释放已申请的内存
内存泄露的方式
- 长生命周期的对象持有短生命周期对象的引用
例如将ArrayList 设置为静态变量,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏 - 连接未关闭
如数据库连接、网络连接和IO 连接等,只有连接被关闭后,垃圾回收器才会回收对应的对象。 - 变量作用域不合理
a.一个变量的定义的作用范围大于其使用范围,b.如果没有及时地把对象设置为null - 内部类持有外部类
Java 的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏,解决方案可以在内部类的内部显示持有一个外部类的软引用(或弱引用),并通过构造方法的方式传递进来,在内部类的使用过程中,先判断一下外部类是否被回收 - Hash值改变
在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露
溢出和泄露避免
内存溢出:检查代码以及设置足够的 空间
内存泄露:一定是代码问题, 很多情况下内存溢出是内存泄露造成的
浅堆(Shallow Heap)
是指一个对象所消耗的内存。例如,在32 位系统中,一个对象引用会占据4个字节,一个int 类型会占据4 个字节,long 型变量会占据8 个字节,每个对象头需要占用8 个字节。
深堆(Retained Heap)
这个对象被GC 回收后,可以真实释放的内存大小,也就是只能通过对象被直接或间接访问到的所有对象的集合。通俗地说,就是指仅被对象所持有的对象的集合。深堆是指对象的保留集中所有的对象的浅堆大小之和。
如A引用B和C,那么A的浅堆大小就是A本身,A的深堆大小是A+B+C
Incoming
当前对象引用了谁
Outgoing
谁引用了当前对象
命令行工具
jps
列出当前机器上正在运行的虚拟机进程,JPS 从操作系统的临时目录上去找
jstat
于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT 编译等运行数据,在没有GUI
图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具
jinfo
查看和修改虚拟机的参数
jmap
用于生成堆转储快照(一般称为heapdump 或dump 文件)。jmap 的作用并不仅仅是为了获取dump 文件,它还可以查询finalize 执行队列、Java 堆和永
久代的详细信息,如空间使用率、当前用的是哪种收集器等。和jinfo 命令一样,jmap 有不少功能在Windows 平台下都是受限的,除了生成dump 文件的-dump 选项和用于查看每个类的实例、空间占用统计的-histo 选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris 下使用。
jstack
命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主
要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。