jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华


jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华
文章图片
jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华
文章图片
jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华
文章图片
jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华
文章图片
jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华
文章图片
Hello , 今天给各位童鞋们分享的是JVM , 赶紧拿出小本子记下来吧
一、内存溢出内存溢出的原因:程序在申请内存时 , 没有足够的空间 。
1. 栈溢出
方法循环递归调用(StackOverflowError)、不断建立线程(OutOfMemoryError) 。
2. 堆溢出
不断创建对象 , 分配对象大于最大堆的大小(OutOfMemoryError) 。
3. 直接内存
JVM 分配的本地直接内存大小大于 JVM 的限制 , 可以通过-XX:MaxDirectMemorySize 来设置(不设置的话默认与堆内存最大值一样也会出现OOM 异常) 。
4. 方法区溢出
一个类要被垃圾收集器回收掉 , 判定条件是比较苛刻的 , 在经常动态生产大量 Class 的应用中 , CGLIb 字节码增强 , 动态语言 , 大量 JSP(JSP 第一次运行需要编译成 Java 类)基于 OSGi 的应用(同一个类 , 被不同的加载器加载也会设为不同的类) , 都可能会导致OOM 。
二、内存泄露程序在申请内存后 , 无法释放已申请的内存空间 , 导致这一部分的原因主要是代码写不合理 , 比如以下几种情况 。
1. 长生命周期的对象持有短生命周期对象的引用
例如将 ArrayList 设置为静态变量 , 然后不断地向ArrayList中添加对象 , 则 ArrayList 容器中的对象在程序结束之前将不能被释放 , 从而造成内存泄漏 。
2. 连接未关闭
如数据库连接、网络连接和 IO 连接等 , 只有连接被关闭后 , 垃圾回收器才会回收对应的对象 。
3. 变量作用域不合理
例如:
【jvm|超赞!不愧是美团内部的JVM学习手册,从头到尾全是精华】一个变量的定义的作用范围大于其使用范围 。
如果没有及时地把对象设置为 null 。
4. 内部类持有外部类
Java 的 非静态内部类 的这种创建方式 , 会隐式地持有外部类的引用 , 而且默认情况下这个引用是强引用 , 因此 , 如果内部类的生命周期长于外部类的生命周期 , 程序很容易就产生内存泄露(可以理解为:垃圾回收器会回收掉外部类的实例 , 但由于内部类持有外部类的引用 , 导致垃圾回收器不能正常工作) 。
解决办法:将非静态内部类改为 静态内部类 , 即加上 static 修饰 , 例如:
5. Hash值改变
在集合中 , 如果修改了对象中的那些参与计算哈希值的字段 , 会导致无法从集合中单独删除当前对象 , 造成内存泄露 。
使用例子来说明 。
三、内存溢出和内存泄漏辨析

  • 内存溢出:实实在在的内存空间不足导致 。
  • 内存泄漏:该释放的对象没有释放 , 常见于使用容器保存元素的情况下 。
如何避免:
  • 内存溢出:检查代码以及设置足够的空间 。
  • 内存泄漏:一定是代码有问题 , 往往很多情况下 , 内存溢出往往是内存泄漏造成的 。
四、了解MATmat是一个内存泄露的分析工具 。
1. 浅堆和深堆
  • 浅堆(Shallow Heap):是指一个对象所消耗的内存 。
  • 深堆(Retained Heap):这个对象被 GC 回收后 , 可以真实释放的内存大小 , 也就是只能通过对象被直接或间接访问到的所有对象的集合 。 通俗地说 , 就是一个对象包含(引用)的所有对象的大小 , 如图:

2. MAT的使用
内存溢出例子演示
参数说明:
-Xms5m 堆初始大小5M
-Xmx5m 堆最大大小5M
-XX:+PrintGCDetails 打印gc日志详情
-XX:+HeapDumpOnOutOfMemoryError 输出内存溢出文件
-XX:HeapDumpPath=D:/oomDump/dump.hprof 内存溢出文件保存位置 , 此文件用于MAT分析
设置参数运行后 , 内存溢出 , 程序结束 , 然后我们就可以用下载好的MAT来分析了 , 当然MAT也只是分析猜想 , 并不代表一定是这个原因导致内存溢出 。