JVM相关学习记录与总结(内存&GC&类加载&工具)
来源:互联网 发布:遗传算法解决线路优化 编辑:程序博客网 时间:2024/05/01 07:40
一、内存
分布
1 总分布图
2 堆内存分布
OOM
- 类别
- 堆内存不足
- 本地内存不足
- 加载类的Perm区内存不足
- 原因
- 内存泄漏
- 空间设置太小
- 大量长期的大对象
内存泄漏
- 症状
- 系统越来越慢,并伴随着CPU使用率过高。这主要是因为随着内存的泄漏,可用的内存越来越小,垃圾回收器频繁进行垃圾回收(FullGC一次接一次,每次耗时几秒甚至几十秒)。而垃圾回收是一个CPU密集型操作,频繁的GC会导致CPU持续居高不下,在有内存泄漏的场合,到了最后必然是伴随着CPU使用率几乎为100
- 系统运行一段时间,系统抛出OOM异常
- 虚拟机core dump
- 常见的原因
- 自定义类加载器。每个对象有一个它class的引用,相应也有它classloader的引用。反过来,类加载器也拥有它加载的所有class的引用,一旦类加载器内存泄漏,意味着一堆对象泄漏。
- 数组减长度,但内容不减。
- Threadlocal变量不释放。
- 静态hash做缓存只增不减。
- 资源(连接池)不关闭。
- 自定义类加载器。每个对象有一个它class的引用,相应也有它classloader的引用。反过来,类加载器也拥有它加载的所有class的引用,一旦类加载器内存泄漏,意味着一堆对象泄漏。
大小的经验推荐
二、GC
CMS
- 标记方式:通过bitmap
- 触发原因:
- 正常触发
- 设置UseCMSInitiatingOccupancyOnly+CMSInitiatingOccupancyFraction=80后只有当老年代空间占用达到80%时才触发CMS
- 设置CMSClassUnloadingEnabled后同时会清理perm区域
- 过程
- 初始标记:为了收集应用程序的对象引用需要暂停应用程序线程,只标记root对象(bitmap里面)【stop the world】。
- 并发标记:从第一阶段收集到的对象引用开始,遍历所有其他的活对象引用。
- 并发预清理:改变当运行第二阶段时,由应用程序线程产生的对象引用,以更新第二阶段的结果。
- 重标记:由于第三阶段是并发的,对象引用可能会发生进一步改变。因此,应用程序线程会再一次被暂停以更新这些变化,并且在进行实际的清理之前确保一个正确的对象引用视图。主要遍历young区和old/perm区的卡表(card table)【stop the world】。
- 并发清理:单线程处理,所有不再被应用的对象将从堆里清除掉,不压缩,所以会有碎片。
- 并发重置:收集器做一些收尾的工作,以便下一次GC周期能有一个干净的状态。
- 与fullGC的关系
- FullGC指对老年代/永久代的stop the world的GC
- FullGC的时间和次数分别指老年代GC时stop the world的时间和次数
- CMS不等于FullGC,CMS分为多个阶段,只有stop the world的阶段被计算到了FullGC的次数和时间,而和业务线程并发的GC的次数和时间是不被认为为Full GC
ParNew(CMS处理年轻代的算法)
- 根据对象的引用关系,从root对象开始,只处理活的对象,采用move©的策略将活的对象都拷贝到to区域;old区对young区对象的引用是通过card table,遍历card table将可以引用到的活的对象也复制到to区域。
- Eden满时触发,多线程处理。
Mark-Sweep-Compact(CMS处理老年代的算法,进行碎片压缩)
过程
- 标记所有活的对象阶段。这个阶段根据root来地柜地标记所有活的对象,基本上等级于CMS的初始和并发标记的,但是是单线程的。
- 计算活着的对象新的内存地址阶段。根据阶段1的结果,单线程遍历每个generation的每个内存区域的每个对象,计算活着的对象应该被compact到哪里,并把计算结果保存到header的mark上。
- 修改对象内部引用。顺序遍历每个generation每个内存区域每个对象,对于每个活着的对象,修改内部引用指向正确的内存地址。
- 移动对象。根据2把活着的对象copy到正确的内存位置。
触发
- promotion failed
- concurrent mode failure
- System.gc()
GC的参数配置
1 堆配置相关
- -Xms4g //最小堆
- -Xmx4g //最大堆
- -Xmn2g //young区大小
- -XX:SurvivorRatio=10 //eden与survivor区大小比例
2 perm配置相关
- -XX:PermSize=256m
- -XX:MaxPermSize=256m
3 压缩指针
- -XX:UseCompressedOops //64位下的内存设置,如果放到32位上(一般就4G),有时空间就超了,压缩指针可以一定程度帮助压缩来适应32位的大小
4 CMS配置相关
- -XX:+UseConcMarkSweepGC
- -XX:+UseCMSCompactAtFullCollection //cms时进行碎片压缩
- -XX:CMSMaxAbortablePrecleanTime=5000
- -XX:+CMSClassUnloadingEnabled //CMS时也会清理perm区
- -XX:CMSInitiatingOccupancyFraction=80 //指定老年代占比多少(这里是80%)时触发CMS
- -XX:+UseCMSInitiatingOccupancyOnly //结合XX:CMSInitiatingOccupancyFraction一起使用
5 GC线程配置相关
- -XX:ParallelGCThreads//设置并发阶段GC的线程的数量
6 GC LOG相关
- -Xloggc:${LOGS}/gc.log
- -XX:+PrintGCDetails
- -XX:+PrintGCDateStamps
7 其他
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath=${LOGS}/java.hprof
三、类加载
过程
其中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的“开始”(仅仅指的是开始,而非执行或者结束,因为这些阶段通常都是互相交叉的混合进行,通常会在一个阶段执行的过程中调用或者激活另一个阶段),而解析阶段则不一定(它在某些情况下可以在初始化阶段之后再开始),这是为了支持Java语言的运行时绑定。
零碎知识点
- 显示与隐式:new是隐式加载,触发
<init>
和<clinit>
;Class.forName是显式,触发<clinit>
。 - 双亲委派:先交给父加载器加载,如果加载不了,就由自己来加载,如果所有都不能加载就抛出ClassNotFoundException异常。
类初始化
- 触发条件
- 使用new关键字实例化对象的时候。读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
- 对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
- 当初始化一个类时,发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()的方法的那个类),虚拟机会先初始化这个主类。
- 内部过程
- 就是执行,编译器将所有类变量初始化语句和静态代码块都收集到中
常见错误
- ClassNotFoundException
- 从自定义类加载器到系统类加载器,到扩展类加载器,到引导类加载器,在不同路径下都找不到那个class
- 如果那个class的确存在,也有两个可能导致这个异常,一是jar冲突了,实际加载的那个class不是你想要的;二是不同的类加载器隔离了,当前类A引用了类B,类A的加载器加载了类A后,因为调用也去加载类B,但是类B指定只能是被ClassLoaderB来加载的,这是加载不到类B也会报这个异常。
- NoClassDefFoundError
- 这个出现的场景一般是类A引用了类B,类A加载器加载类B时加载失败(不存在或者加载器隔离),这时会报ClassNotFoundException异常,并连带引发NoClassDefFoundError这个错误。也就是说在编译时这个类是能够被找到的,但是在执行时却没有找到。
- 【摘自java虚拟机规范】如果 Java 虚拟机曾经试图在 D 的验证或解析阶段、但又还没有进 行初始化时加载 C 类,当用于加载 C 的初始类加载器抛出 ClassNotFoundException 实例时,Java 虚拟机在D中必须抛出NoClassDefFoundError异常,它的 cause 字段中就保存了那个ClassNotFoundException异常实例。
- LinkageError
- 此类已经在ClassLoader加载过了,重复的加载会造成这个异常
- 由于JVM的这个保护机制,使得在JVM中是没办法直接更新一个已经load了的Class的,只能是创建一个新的ClassLoader来加载更新了的Class,然后将新的请求转入到这个ClassLoader中来获取类,这也是JVM中不好实现动态更新的原因之一,而其他更多的原因是对象状态的复制、依赖的设置等等
工具
- jstack
- jstack java_pid > jstack.out //输出堆栈信息
- jmap
- jmap -histo java_pid > mem.out//查看内存使用情况,某个类有多少实例,占用多少内存等
- top
- top -H -p java_pid //可以查看本地线程资源占用情况
- printf("0x%x",nid)//将本地线程id转成16进制,方便在线程堆栈中搜索出对应的java线程
- jps
- jps -v //输出java_pid
- jstat
- jstat -gc/gcutil/gccapacity/gccause java_pid time_interval times//输出gc状态,可以指定输出次数和输出的时间间隔
- jinfo
- 获取java配置信息
参考
追风堂公共课:《初探CMS GC算法原理和实例分析》 JVM交流答疑圈-问答归档(20140228)
线上jvm参数分析
理解Hotspot JVM CMS垃圾回收器
0 0
- JVM相关学习记录与总结(内存&GC&类加载&工具)
- jvm学习记录:GC
- 回顾总结一下JVM(组成结构、GC、类加载)
- JVM内存与GC
- jvm内存与gc
- jvm内存管理与gc
- JVM内存分配与GC
- 【java学习】垃圾回收机制(GC)、与C#对比、JVM内存学习
- JVM内核学习 --内存相关,内存结构, GC,ClassLoader,内存溢出
- JVM参数设置(GC相关)
- JVM类加载机制学习记录
- jvm学习记录 -- Java内存区域与内存溢出异常
- JVM内存结构以及GC原理学习
- jvm内存划分与GC工作机制
- JVM内存划分与GC机制
- JVM内存划分与GC机制
- JVM内存模型与GC算法
- JVM内存概览与GC初步
- 如果你也身在小团队,该选择哪款协作工具?
- 在 Ubuntu 虚拟机中安装 VMware Tools
- 写给Android开发者的混淆使用手册
- Session应用:验证码技术
- 根据url下载apk
- JVM相关学习记录与总结(内存&GC&类加载&工具)
- 带导航的控制器上面再加个带导航的控制器,但第二个导航只从屏幕左边隔一定距离显示到右边的框
- Windows 服务移植到Mono
- Struts2(四)-知识清单
- 使用VideoCapture类调用摄像头读入视频并显示
- nginx+tomcat+redis做集群负载均衡
- 调用了opencv的C++程序调试运行没有问题,但一运行exe就报错debug error R6010
- 基于python3的k-means代码实现
- opencv Findcontours异常