1.JVM内存模型
2.类加载
2.1 加载
- 通过一个类的全限定名来获取定义次类的二进制流(ZIP 包、网络、运算生成、JSP 生成、数据库读取)。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法去这个类的各种数据的访问入口。
2.2 验证
是连接的第一步,确保 Class 文件的字节流中包含的信息符合当前虚拟机要求。
2.3 准备
这个阶段正式为类分配内存并设置类变量初始值,内存在方法区中分配。
2.4 解析
虚拟机将常量池内的符号引用替换为直接引用的过程。
2.5 初始化
开始执行类中的 Java 代码。
3.双亲委派模型
双亲委派模型:
当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。 具备了层级关系
如果类自己加载的话,如果用户自己编写了一个Object类,那可能存在多个不同的Object类,java类型体系中最基础的行为也就无法保证。
4.什么可以用作GCroot
可作为 GC Roots 的对象:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中 JNI(即一般说的 Native 方法) 引用的对象
5.垃圾回收器
新生代ParNew(复制)+老年代CMS(最短停顿时间 ,初始标记、并发标记、重新标记、并发清除)
6.怎么判断对象是否可以被回收?
两种方法:
引用计数器法:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析算法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
7.说一下 JVM 有哪些垃圾回收算法?
标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
8.垃圾回收机制
JVM堆分为新生代、老年代和永久代。垃圾回收主要是发生在新生代和老年代。
其中新生代分为Eden区和两个survivor区,系统运行会把对象创建到Eden区,采用复制算法进行回收。即每次YoungGC 会标记存活的对象,复制到survivor0中,再次YoungGC时,再把存活对象复制到survivor1中。会保证一直有一个survivor是空着的。
新生代使用的垃圾回收器:ParNew
新生代GC触发条件:Eden区无法放下更多对象
老年代的垃圾回收器:CMS
大对象直接进入老年代、长期存活对象(经过数次youngGC,年龄达到设置值)将进入老年代
9.如何排查内存溢出OOM
1.获取dump文件
登上服务器,用 Java自带的jmap生成dump 文件,命令如下:
jmap -dump:live,format=b,file= heap.hprof <pid>
2. 分析找出占用内存大的实例
将dump文件下载到自己电脑,用 MAT分析工具打开。
找到占用内存大的实例,则可以初步判定为导致内存溢出的类
3.代码分析
看代码是否存在大量无法回收没有释放的资源
例如:
- 申请完资源后,未调用close()或dispose()释放资源
- 消费者消费速度慢(或停止消费了),而生产者不断往队列中投递任务,导致队列中任务累积过多
你们项⽬如何排查JVM问题
JVM有回收机制为什么还会内存泄露
内存泄露主要存在两种情况:
1.堆中申请的空间没有被释放(垃圾回收机制解决)
2.对象已经不在被使用,但仍然在内存中保留
引起的原因:
1.静态集合类如hashmap等 随对象生命周期
2.各种链接,io、数据库、网络等链接没有释放
3.内存中加载的数据量过于庞大,如一次从数据库中取出过多的数据。
4.代码中存在死循环或循环中产生过多重复的对象实体。
5.启动参数内存值设置的过小。
解决方案:
1.修改JVM启动参数,直接增加JVM内存。
2.检查错误日志,查看“OutOfMemory”是否有其他的异常或错误。
3.对代码进行走查和分析,找出可能发生内存溢出的位置。
重点排查以下几点:
1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
2.检查代码中是否有死循环或递归调用。
3.检查是否有大循环重复产生新对象实体。
4.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
原文链接:https://blog.youkuaiyun.com/weixin_43375482/article/details/99474197